github.com/xmidt-org/webpa-common@v1.11.9/secure/validator_test.go (about)

     1  package secure
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/SermoDigital/jose"
    11  	"github.com/SermoDigital/jose/jws"
    12  	"github.com/SermoDigital/jose/jwt"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/mock"
    15  	"github.com/xmidt-org/webpa-common/secure/key"
    16  )
    17  
    18  func ExampleSimpleJWSValidator(t *testing.T) {
    19  	// A basic validator with useful defaults
    20  	// We need to use the publicKeyResolver, as that's what validates
    21  	// the JWS signed with the private key
    22  
    23  	assert := assert.New(t)
    24  
    25  	validator := JWSValidator{
    26  		Resolver: publicKeyResolver,
    27  	}
    28  
    29  	token := &Token{
    30  		tokenType: Bearer,
    31  		value:     string(testSerializedJWT),
    32  	}
    33  
    34  	ctx := context.Background()
    35  	ctx = context.WithValue(ctx, "method", "post")
    36  	ctx = context.WithValue(ctx, "path", "/api/foo/path")
    37  
    38  	valid, err := validator.Validate(ctx, token)
    39  
    40  	assert.True(valid)
    41  	assert.Nil(err)
    42  }
    43  
    44  func TestValidatorFunc(t *testing.T) {
    45  	assert := assert.New(t)
    46  	expectedError := errors.New("expected")
    47  	var validator Validator = ValidatorFunc(func(ctx context.Context, token *Token) (bool, error) { return false, expectedError })
    48  
    49  	valid, err := validator.Validate(nil, nil)
    50  	assert.False(valid)
    51  	assert.Equal(expectedError, err)
    52  }
    53  
    54  func TestValidators(t *testing.T) {
    55  	assert := assert.New(t)
    56  	var testData = [][]bool{
    57  		{true},
    58  		{false},
    59  		{true, false},
    60  		{false, true},
    61  		{true, false, false},
    62  		{false, true, false},
    63  		{false, false, true},
    64  	}
    65  
    66  	for _, record := range testData {
    67  		t.Logf("%v", record)
    68  		token := &Token{}
    69  		mocks := make([]interface{}, 0, len(record))
    70  		validators := make(Validators, 0, len(record))
    71  
    72  		// synthesize a chain of validators:
    73  		// one mock for each entry.  until "true" is found,
    74  		// validators should be called.  afterward, none
    75  		// should be called.
    76  		var (
    77  			expectedValid bool
    78  			expectedError error
    79  		)
    80  
    81  		for index, success := range record {
    82  			mockValidator := &MockValidator{}
    83  			mocks = append(mocks, mockValidator.Mock)
    84  			validators = append(validators, mockValidator)
    85  
    86  			if !expectedValid {
    87  				expectedValid = success
    88  				if success {
    89  					expectedError = nil
    90  				} else {
    91  					expectedError = fmt.Errorf("expected validator error #%d", index)
    92  				}
    93  
    94  				mockValidator.On("Validate", nil, token).Return(expectedValid, expectedError).Once()
    95  			}
    96  		}
    97  
    98  		valid, err := validators.Validate(nil, token)
    99  		assert.Equal(expectedValid, valid)
   100  		assert.Equal(expectedError, err)
   101  
   102  		mock.AssertExpectationsForObjects(t, mocks...)
   103  	}
   104  }
   105  
   106  func TestExactMatchValidator(t *testing.T) {
   107  	assert := assert.New(t)
   108  
   109  	token := &Token{
   110  		tokenType: Basic,
   111  		value:     "dGVzdDp0ZXN0Cg==",
   112  	}
   113  
   114  	successValidator := ExactMatchValidator(token.value)
   115  	assert.NotNil(successValidator)
   116  
   117  	valid, err := successValidator.Validate(nil, token)
   118  	assert.True(valid)
   119  	assert.Nil(err)
   120  
   121  	failureValidator := ExactMatchValidator("this should not be valid")
   122  	assert.NotNil(failureValidator)
   123  
   124  	valid, err = failureValidator.Validate(nil, token)
   125  	assert.False(valid)
   126  	assert.Nil(err)
   127  }
   128  
   129  func TestJWSValidatorInvalidTokenType(t *testing.T) {
   130  	assert := assert.New(t)
   131  
   132  	mockJWSParser := &mockJWSParser{}
   133  	mockResolver := &key.MockResolver{}
   134  	validator := &JWSValidator{
   135  		Parser:   mockJWSParser,
   136  		Resolver: mockResolver,
   137  	}
   138  
   139  	token := &Token{
   140  		tokenType: Basic,
   141  		value:     "does not matter",
   142  	}
   143  
   144  	valid, err := validator.Validate(nil, token)
   145  	assert.False(valid)
   146  	assert.Nil(err)
   147  
   148  	mockJWSParser.AssertExpectations(t)
   149  	mockResolver.AssertExpectations(t)
   150  }
   151  
   152  func TestJWSValidatorInvalidJWT(t *testing.T) {
   153  	assert := assert.New(t)
   154  
   155  	mockJWSParser := &mockJWSParser{}
   156  	mockResolver := &key.MockResolver{}
   157  	validator := &JWSValidator{
   158  		Parser:   mockJWSParser,
   159  		Resolver: mockResolver,
   160  	}
   161  
   162  	expectedError := errors.New("expected")
   163  	token := &Token{
   164  		tokenType: Bearer,
   165  		value:     "",
   166  	}
   167  
   168  	mockJWSParser.On("ParseJWS", token).Return(nil, expectedError).Once()
   169  	valid, err := validator.Validate(nil, token)
   170  	assert.False(valid)
   171  	assert.Equal(expectedError, err)
   172  
   173  	mockJWSParser.AssertExpectations(t)
   174  	mockResolver.AssertExpectations(t)
   175  }
   176  
   177  func TestJWSValidatorNoProtectedHeader(t *testing.T) {
   178  	assert := assert.New(t)
   179  
   180  	for _, empty := range []jose.Protected{nil, {}} {
   181  		t.Logf("empty Protected header: %v", empty)
   182  		token := &Token{tokenType: Bearer, value: "does not matter"}
   183  		mockResolver := &key.MockResolver{}
   184  
   185  		mockJWS := &mockJWS{}
   186  		mockJWS.On("Protected").Return(empty).Once()
   187  
   188  		mockJWSParser := &mockJWSParser{}
   189  		mockJWSParser.On("ParseJWS", token).Return(mockJWS, nil).Once()
   190  
   191  		validator := &JWSValidator{
   192  			Resolver: mockResolver,
   193  			Parser:   mockJWSParser,
   194  		}
   195  
   196  		valid, err := validator.Validate(nil, token)
   197  		assert.False(valid)
   198  		assert.Equal(err, ErrorNoProtectedHeader)
   199  
   200  		mockResolver.AssertExpectations(t)
   201  		mockJWS.AssertExpectations(t)
   202  		mockJWSParser.AssertExpectations(t)
   203  	}
   204  }
   205  
   206  func TestJWSValidatorNoSigningMethod(t *testing.T) {
   207  	assert := assert.New(t)
   208  
   209  	for _, badAlg := range []interface{}{nil, "", "this is not a valid signing method"} {
   210  		t.Logf("badAlg: %v", badAlg)
   211  		token := &Token{tokenType: Bearer, value: "does not matter"}
   212  		mockResolver := &key.MockResolver{}
   213  
   214  		mockJWS := &mockJWS{}
   215  		mockJWS.On("Protected").Return(jose.Protected{"alg": badAlg}).Once()
   216  
   217  		mockJWSParser := &mockJWSParser{}
   218  		mockJWSParser.On("ParseJWS", token).Return(mockJWS, nil).Once()
   219  
   220  		validator := &JWSValidator{
   221  			Resolver: mockResolver,
   222  			Parser:   mockJWSParser,
   223  		}
   224  
   225  		valid, err := validator.Validate(nil, token)
   226  		assert.False(valid)
   227  		assert.Equal(err, ErrorNoSigningMethod)
   228  
   229  		mockResolver.AssertExpectations(t)
   230  		mockJWS.AssertExpectations(t)
   231  		mockJWSParser.AssertExpectations(t)
   232  	}
   233  }
   234  
   235  //func TestJWSValidatorCapabilities(t *testing.T) {
   236  //	assert := assert.New(t)
   237  //
   238  //	defaultClaims := jws.Claims{
   239  //		"capabilities": []interface{}{
   240  //			"x1:webpa:api:.*:all",
   241  //			"x1:webpa:api:device/.*/config/.*:all",
   242  //			"x1:webpa:api:device/.*/config/.*:get",
   243  //			"x1:webpa:api:device/.*/stat:get",
   244  //			"x1:webpa:api:hook:post",
   245  //			"x1:webpa:api:hooks:get",
   246  //		},
   247  //	}
   248  //
   249  //	ctxValid := context.Background()
   250  //	ctxValid = context.WithValue(ctxValid, "method", "post")
   251  //	ctxValid = context.WithValue(ctxValid, "path", "/api/foo/path")
   252  //
   253  //	ctxInvalidMethod := context.Background()
   254  //	ctxInvalidMethod = context.WithValue(ctxInvalidMethod, "method", "get")
   255  //	ctxInvalidMethod = context.WithValue(ctxInvalidMethod, "path", "/api/foo/path")
   256  //
   257  //	ctxInvalidPath := context.Background()
   258  //	ctxInvalidPath = context.WithValue(ctxInvalidPath, "method", "post")
   259  //	ctxInvalidPath = context.WithValue(ctxInvalidPath, "path", "/ipa/foo/path")
   260  //
   261  //	ctxInvalidApi := context.Background()
   262  //	ctxInvalidApi = context.WithValue(ctxInvalidApi, "method", "get")
   263  //	ctxInvalidApi = context.WithValue(ctxInvalidApi, "path", "/api")
   264  //
   265  //	ctxInvalidVersion := context.Background()
   266  //	ctxInvalidVersion = context.WithValue(ctxInvalidVersion, "method", "get")
   267  //	ctxInvalidVersion = context.WithValue(ctxInvalidVersion, "path", "/api/v2")
   268  //
   269  //	ctxValidConfig := context.Background()
   270  //	ctxValidConfig = context.WithValue(ctxValidConfig, "method", "get")
   271  //	ctxValidConfig = context.WithValue(ctxValidConfig, "path", "/api/v2/device/mac:112233445566/config?name=foodoo")
   272  //	validConfigClaims := jws.Claims{
   273  //		"capabilities": []interface{}{
   274  //			"x1:webpa:api:device/.*/config/?.*:get",
   275  //		},
   276  //	}
   277  //
   278  //	ctxValidConfig2 := context.Background()
   279  //	ctxValidConfig2 = context.WithValue(ctxValidConfig, "method", "get")
   280  //	ctxValidConfig2 = context.WithValue(ctxValidConfig, "path", "/api/v2/device/mac:112233445566/config")
   281  //
   282  //	ctxValidConfig3 := context.Background()
   283  //	ctxValidConfig3 = context.WithValue(ctxValidConfig, "method", "get")
   284  //	ctxValidConfig3 = context.WithValue(ctxValidConfig, "path", "/api/v2/device/mac:112233445566/config/")
   285  //
   286  //	ctxValidConfig4 := context.Background()
   287  //	ctxValidConfig4 = context.WithValue(ctxValidConfig, "method", "get")
   288  //	ctxValidConfig4 = context.WithValue(ctxValidConfig, "path", "/api/v2/device/mac:112233445566/config/bob")
   289  //
   290  //	validConfigClaims2 := jws.Claims{
   291  //		"capabilities": []interface{}{
   292  //			"x1:webpa:api:device/.*/config\\b:get",
   293  //		},
   294  //	}
   295  //
   296  //	ctxInvalidConfig := context.Background()
   297  //	ctxInvalidConfig = context.WithValue(ctxInvalidConfig, "method", "get")
   298  //	ctxInvalidConfig = context.WithValue(ctxInvalidConfig, "path", "/api/v2/device/mac:112233445566/config?name=foodoo")
   299  //	invalidConfigClaims := jws.Claims{
   300  //		"capabilities": []interface{}{
   301  //			"x1:webpa:api:device/.*/config/.*:get",
   302  //		},
   303  //	}
   304  //
   305  //	ctxInvalidConfig2 := context.Background()
   306  //	ctxInvalidConfig2 = context.WithValue(ctxValidConfig, "method", "get")
   307  //	ctxInvalidConfig2 = context.WithValue(ctxValidConfig, "path", "/api/v2/device/mac:112233445566/configure")
   308  //
   309  //	ctxInvalidConfig3 := context.Background()
   310  //	ctxInvalidConfig3 = context.WithValue(ctxValidConfig, "method", "get")
   311  //	ctxInvalidConfig3 = context.WithValue(ctxValidConfig, "path", "/api/v2/device/mac:112233445566/configure/")
   312  //
   313  //	ctxValidHook := context.Background()
   314  //	ctxValidHook = context.WithValue(ctxValidHook, "method", "post")
   315  //	ctxValidHook = context.WithValue(ctxValidHook, "path", "/api/v2/hook")
   316  //
   317  //	ctxValidHooks := context.Background()
   318  //	ctxValidHooks = context.WithValue(ctxValidHooks, "method", "get")
   319  //	ctxValidHooks = context.WithValue(ctxValidHooks, "path", "/api/v2/hooks")
   320  //
   321  //	ctxInvalidHealth := context.Background()
   322  //	ctxInvalidHealth = context.WithValue(ctxInvalidHealth, "method", "get")
   323  //	ctxInvalidHealth = context.WithValue(ctxInvalidHealth, "path", "/health")
   324  //
   325  //	ctxValidEvent := context.Background()
   326  //	ctxValidEvent = context.WithValue(ctxValidEvent, "method", "post")
   327  //	ctxValidEvent = context.WithValue(ctxValidEvent, "path", "/api/v2/notify/mac:112233445566/event/device-status")
   328  //
   329  //	ctxValidStat := context.Background()
   330  //	ctxValidStat = context.WithValue(ctxValidStat, "method", "get")
   331  //	ctxValidStat = context.WithValue(ctxValidStat, "path", "/api/v2/device/mac:112233445566/stat")
   332  //
   333  //	validStatClaims := jws.Claims{
   334  //		"capabilities": []interface{}{
   335  //			"x1:webpa:api:device/.*/stat:get",
   336  //		},
   337  //	}
   338  //
   339  //	var testData = []struct {
   340  //		context       context.Context
   341  //		claims        jws.Claims
   342  //		expectedValid bool
   343  //	}{
   344  //		{ctxValid, defaultClaims, true},
   345  //		{context.Background(), defaultClaims, false},
   346  //		{ctxInvalidMethod, testClaims, false},
   347  //		{ctxInvalidPath, defaultClaims, false},
   348  //		{ctxInvalidApi, defaultClaims, false},
   349  //		{ctxInvalidVersion, defaultClaims, false},
   350  //		{ctxValidConfig, validConfigClaims, true},
   351  //
   352  //		{ctxValidConfig2, validConfigClaims, true},
   353  //		{ctxValidConfig3, validConfigClaims, true},
   354  //		{ctxValidConfig4, validConfigClaims, true},
   355  //		{ctxValidConfig, validConfigClaims2, true},
   356  //		{ctxValidConfig2, validConfigClaims2, true},
   357  //		{ctxValidConfig3, validConfigClaims2, true},
   358  //		{ctxValidConfig4, validConfigClaims2, true},
   359  //
   360  //		{ctxInvalidConfig, invalidConfigClaims, false},
   361  //
   362  //		{ctxInvalidConfig2, validConfigClaims, true},
   363  //		{ctxInvalidConfig3, validConfigClaims, true},
   364  //		{ctxInvalidConfig2, validConfigClaims2, false},
   365  //		{ctxInvalidConfig3, validConfigClaims2, false},
   366  //
   367  //		{ctxValidHook, defaultClaims, true},
   368  //		{ctxValidHooks, defaultClaims, true},
   369  //		{ctxInvalidHealth, defaultClaims, false},
   370  //		{ctxValidEvent, defaultClaims, true},
   371  //		{ctxValidStat, validStatClaims, true},
   372  //	}
   373  //
   374  //	for _, record := range testData {
   375  //		var ok bool
   376  //		var method, path string
   377  //		if method, ok = record.context.Value("method").(string); ok {
   378  //			method = record.context.Value("method").(string)
   379  //		}
   380  //		if path, ok = record.context.Value("path").(string); ok {
   381  //			path = record.context.Value("path").(string)
   382  //		}
   383  //
   384  //		t.Logf("ctx method: %s, ctx path: %s, claims: %v, expectedValid: %v", method, path, record.claims, record.expectedValid)
   385  //		token := &Token{tokenType: Bearer, value: "does not matter"}
   386  //
   387  //		mockPair := &key.MockPair{}
   388  //		expectedPublicKey := interface{}(123)
   389  //		mockPair.On("Public").Return(expectedPublicKey).Once()
   390  //
   391  //		mockResolver := &key.MockResolver{}
   392  //		mockResolver.On("ResolveKey", mock.AnythingOfType("string")).Return(mockPair, nil).Once()
   393  //
   394  //		expectedSigningMethod := jws.GetSigningMethod("RS256")
   395  //		assert.NotNil(expectedSigningMethod)
   396  //
   397  //		mockJWS := &mockJWS{}
   398  //		mockJWS.On("Protected").Return(jose.Protected{"alg": "RS256"}).Once()
   399  //		mockJWS.On("Verify", expectedPublicKey, expectedSigningMethod).Return(nil).Once()
   400  //		mockJWS.On("Payload").Return(record.claims).Once()
   401  //
   402  //		mockJWSParser := &mockJWSParser{}
   403  //		mockJWSParser.On("ParseJWS", token).Return(mockJWS, nil).Once()
   404  //
   405  //		validator := &JWSValidator{
   406  //			Resolver: mockResolver,
   407  //			Parser:   mockJWSParser,
   408  //		}
   409  //
   410  //		valid, err := validator.Validate(record.context, token)
   411  //		assert.Equal(record.expectedValid, valid)
   412  //		assert.Nil(err)
   413  //
   414  //		mockPair.AssertExpectations(t)
   415  //		mockResolver.AssertExpectations(t)
   416  //		mockJWS.AssertExpectations(t)
   417  //		mockJWSParser.AssertExpectations(t)
   418  //	}
   419  //}
   420  //
   421  // TestJWSValidatorResolverError also tests the correct key id determination
   422  // when the header has a "kid" field vs the JWSValidator.DefaultKeyId member being set.
   423  func TestJWSValidatorResolverError(t *testing.T) {
   424  	assert := assert.New(t)
   425  
   426  	var testData = []struct {
   427  		headerKeyId   string
   428  		defaultKeyId  string
   429  		expectedKeyId string
   430  	}{
   431  		{"", "", ""},
   432  		{"", "current", "current"},
   433  		{"akey", "", "akey"},
   434  		{"akey", "current", "akey"},
   435  	}
   436  
   437  	for _, record := range testData {
   438  		t.Logf("%#v", record)
   439  		token := &Token{tokenType: Bearer, value: "does not matter"}
   440  
   441  		expectedResolverError := errors.New("expected resolver error")
   442  		mockResolver := &key.MockResolver{}
   443  		mockResolver.On("ResolveKey", record.expectedKeyId).Return(nil, expectedResolverError).Once()
   444  
   445  		mockJWS := &mockJWS{}
   446  		mockJWS.On("Protected").Return(jose.Protected{"alg": "RS256", "kid": record.headerKeyId}).Once()
   447  
   448  		mockJWSParser := &mockJWSParser{}
   449  		mockJWSParser.On("ParseJWS", token).Return(mockJWS, nil).Once()
   450  
   451  		validator := &JWSValidator{
   452  			Resolver:     mockResolver,
   453  			Parser:       mockJWSParser,
   454  			DefaultKeyId: record.defaultKeyId,
   455  		}
   456  
   457  		valid, err := validator.Validate(nil, token)
   458  		assert.False(valid)
   459  		assert.Equal(err, expectedResolverError)
   460  
   461  		mockResolver.AssertExpectations(t)
   462  		mockJWS.AssertExpectations(t)
   463  		mockJWSParser.AssertExpectations(t)
   464  	}
   465  }
   466  
   467  func TestJWSValidatorVerify(t *testing.T) {
   468  	assert := assert.New(t)
   469  
   470  	var testData = []struct {
   471  		expectedValid       bool
   472  		expectedVerifyError error
   473  	}{
   474  		{true, nil},
   475  		{false, errors.New("expected Verify error")},
   476  	}
   477  
   478  	for _, record := range testData {
   479  		t.Logf("%v", record)
   480  		token := &Token{tokenType: Bearer, value: "does not matter"}
   481  
   482  		mockPair := &key.MockPair{}
   483  		expectedPublicKey := interface{}(123)
   484  		mockPair.On("Public").Return(expectedPublicKey).Once()
   485  
   486  		mockResolver := &key.MockResolver{}
   487  		mockResolver.On("ResolveKey", mock.AnythingOfType("string")).Return(mockPair, nil).Once()
   488  
   489  		expectedSigningMethod := jws.GetSigningMethod("RS256")
   490  		assert.NotNil(expectedSigningMethod)
   491  
   492  		mockJWS := &mockJWS{}
   493  		mockJWS.On("Protected").Return(jose.Protected{"alg": "RS256"}).Once()
   494  		mockJWS.On("Verify", expectedPublicKey, expectedSigningMethod).Return(record.expectedVerifyError).Once()
   495  		if record.expectedVerifyError == nil {
   496  			mockJWS.On("Payload").Return(testClaims).Once()
   497  		}
   498  
   499  		mockJWSParser := &mockJWSParser{}
   500  		mockJWSParser.On("ParseJWS", token).Return(mockJWS, nil).Once()
   501  
   502  		validator := &JWSValidator{
   503  			Resolver: mockResolver,
   504  			Parser:   mockJWSParser,
   505  		}
   506  
   507  		ctx := context.Background()
   508  		ctx = context.WithValue(ctx, "method", "post")
   509  		ctx = context.WithValue(ctx, "path", "/api/foo/path")
   510  
   511  		valid, err := validator.Validate(ctx, token)
   512  		assert.Equal(record.expectedValid, valid)
   513  		assert.Equal(record.expectedVerifyError, err)
   514  
   515  		mockPair.AssertExpectations(t)
   516  		mockResolver.AssertExpectations(t)
   517  		mockJWS.AssertExpectations(t)
   518  		mockJWSParser.AssertExpectations(t)
   519  	}
   520  }
   521  
   522  func TestJWSValidatorValidate(t *testing.T) {
   523  	assert := assert.New(t)
   524  
   525  	var testData = []struct {
   526  		expectedValid         bool
   527  		expectedValidateError error
   528  		expectedJWTValidators []*jwt.Validator
   529  	}{
   530  		{true, nil, []*jwt.Validator{{}}},
   531  		{true, nil, []*jwt.Validator{{}, {}}},
   532  		{false, errors.New("expected Validate error 1"), []*jwt.Validator{{}}},
   533  		{false, errors.New("expected Validate error 2"), []*jwt.Validator{{}, {}}},
   534  	}
   535  
   536  	for _, record := range testData {
   537  		t.Logf("%v", record)
   538  		token := &Token{tokenType: Bearer, value: "does not matter"}
   539  
   540  		mockPair := &key.MockPair{}
   541  		expectedPublicKey := interface{}(123)
   542  		mockPair.On("Public").Return(expectedPublicKey).Once()
   543  
   544  		mockResolver := &key.MockResolver{}
   545  		mockResolver.On("ResolveKey", mock.AnythingOfType("string")).Return(mockPair, nil).Once()
   546  
   547  		expectedSigningMethod := jws.GetSigningMethod("RS256")
   548  		assert.NotNil(expectedSigningMethod)
   549  
   550  		mockJWS := &mockJWS{}
   551  		mockJWS.On("Protected").Return(jose.Protected{"alg": "RS256"}).Once()
   552  		mockJWS.On("Validate", expectedPublicKey, expectedSigningMethod, record.expectedJWTValidators).
   553  			Return(record.expectedValidateError).
   554  			Once()
   555  		if record.expectedValidateError == nil {
   556  			mockJWS.On("Payload").Return(testClaims).Once()
   557  		}
   558  
   559  		mockJWSParser := &mockJWSParser{}
   560  		mockJWSParser.On("ParseJWS", token).Return(mockJWS, nil).Once()
   561  
   562  		validator := &JWSValidator{
   563  			Resolver:      mockResolver,
   564  			Parser:        mockJWSParser,
   565  			JWTValidators: record.expectedJWTValidators,
   566  		}
   567  
   568  		ctx := context.Background()
   569  		ctx = context.WithValue(ctx, "method", "post")
   570  		ctx = context.WithValue(ctx, "path", "/api/foo/path")
   571  
   572  		valid, err := validator.Validate(ctx, token)
   573  		assert.Equal(record.expectedValid, valid)
   574  		assert.Equal(record.expectedValidateError, err)
   575  
   576  		mockPair.AssertExpectations(t)
   577  		mockResolver.AssertExpectations(t)
   578  		mockJWS.AssertExpectations(t)
   579  		mockJWSParser.AssertExpectations(t)
   580  	}
   581  }
   582  func TestJWTValidatorFactory(t *testing.T) {
   583  	assert := assert.New(t)
   584  	now := time.Now().Unix()
   585  
   586  	var testData = []struct {
   587  		claims      jwt.Claims
   588  		factory     JWTValidatorFactory
   589  		expectValid bool
   590  	}{
   591  		{
   592  			claims:      jwt.Claims{},
   593  			factory:     JWTValidatorFactory{},
   594  			expectValid: true,
   595  		},
   596  		{
   597  			claims: jwt.Claims{
   598  				"exp": now + 3600,
   599  			},
   600  			factory:     JWTValidatorFactory{},
   601  			expectValid: true,
   602  		},
   603  		{
   604  			claims: jwt.Claims{
   605  				"exp": now - 3600,
   606  			},
   607  			factory:     JWTValidatorFactory{},
   608  			expectValid: false,
   609  		},
   610  		{
   611  			claims: jwt.Claims{
   612  				"exp": now - 200,
   613  			},
   614  			factory: JWTValidatorFactory{
   615  				ExpLeeway: 300,
   616  			},
   617  			expectValid: true,
   618  		},
   619  		{
   620  			claims: jwt.Claims{
   621  				"nbf": now + 3600,
   622  			},
   623  			factory:     JWTValidatorFactory{},
   624  			expectValid: false,
   625  		},
   626  		{
   627  			claims: jwt.Claims{
   628  				"nbf": now - 3600,
   629  			},
   630  			factory:     JWTValidatorFactory{},
   631  			expectValid: true,
   632  		},
   633  		{
   634  			claims: jwt.Claims{
   635  				"nbf": now + 200,
   636  			},
   637  			factory: JWTValidatorFactory{
   638  				NbfLeeway: 300,
   639  			},
   640  			expectValid: true,
   641  		},
   642  	}
   643  
   644  	for _, record := range testData {
   645  		t.Logf("%#v", record)
   646  
   647  		{
   648  			t.Log("Simple case: no custom validate functions")
   649  			validator := record.factory.New()
   650  			assert.NotNil(validator)
   651  			mockJWS := &mockJWS{}
   652  			mockJWS.On("Claims").Return(record.claims).Once()
   653  
   654  			err := validator.Validate(mockJWS)
   655  			assert.Equal(record.expectValid, err == nil)
   656  
   657  			mockJWS.AssertExpectations(t)
   658  		}
   659  
   660  		{
   661  			for _, firstResult := range []error{nil, errors.New("first error")} {
   662  				first := func(jwt.Claims) error {
   663  					return firstResult
   664  				}
   665  
   666  				{
   667  					t.Logf("One custom validate function returning: %v", firstResult)
   668  					validator := record.factory.New(first)
   669  					assert.NotNil(validator)
   670  					mockJWS := &mockJWS{}
   671  					mockJWS.On("Claims").Return(record.claims).Once()
   672  
   673  					err := validator.Validate(mockJWS)
   674  					assert.Equal(record.expectValid && firstResult == nil, err == nil)
   675  
   676  					mockJWS.AssertExpectations(t)
   677  				}
   678  
   679  				for _, secondResult := range []error{nil, errors.New("second error")} {
   680  					second := func(jwt.Claims) error {
   681  						return secondResult
   682  					}
   683  
   684  					{
   685  						t.Logf("Two custom validate functions returning: %v, %v", firstResult, secondResult)
   686  						validator := record.factory.New(first, second)
   687  						assert.NotNil(validator)
   688  						mockJWS := &mockJWS{}
   689  						mockJWS.On("Claims").Return(record.claims).Once()
   690  
   691  						err := validator.Validate(mockJWS)
   692  						assert.Equal(
   693  							record.expectValid && firstResult == nil && secondResult == nil,
   694  							err == nil,
   695  						)
   696  
   697  						mockJWS.AssertExpectations(t)
   698  					}
   699  				}
   700  			}
   701  		}
   702  	}
   703  }
   704  
   705  //A simple verification that a pointer function signature is used
   706  func TestDefineMeasures(t *testing.T) {
   707  	assert := assert.New(t)
   708  	a, m := JWTValidatorFactory{}, &JWTValidationMeasures{}
   709  	a.DefineMeasures(m)
   710  	assert.Equal(m, a.measures)
   711  
   712  	b := JWSValidator{}
   713  	b.DefineMeasures(m)
   714  	assert.Equal(m, b.measures)
   715  }