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 }