github.com/blend/go-sdk@v1.20220411.3/web/auth_manager_test.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package web 9 10 import ( 11 "bytes" 12 "context" 13 "fmt" 14 "net/http" 15 "net/url" 16 "testing" 17 "time" 18 19 "github.com/blend/go-sdk/assert" 20 "github.com/blend/go-sdk/uuid" 21 "github.com/blend/go-sdk/webutil" 22 ) 23 24 func Test_MustNewAuthManager(t *testing.T) { 25 its := assert.New(t) 26 27 am := MustNewAuthManager(OptAuthManagerCookieName("X-FOO")) 28 its.Equal("X-FOO", am.CookieDefaults.Name) 29 30 // test panics 31 var recovered interface{} 32 func() { 33 defer func() { 34 r := recover() 35 if r != nil { 36 recovered = r 37 } 38 }() 39 am = MustNewAuthManager(func(_ *AuthManager) error { return fmt.Errorf("this is just a test") }) 40 }() 41 its.NotNil(recovered) 42 } 43 44 func Test_NewAuthManager(t *testing.T) { 45 its := assert.New(t) 46 47 am, err := NewAuthManager() 48 its.Nil(err) 49 its.Equal(DefaultCookieName, am.CookieDefaults.Name) 50 its.Equal(DefaultCookiePath, am.CookieDefaults.Path) 51 its.Equal(DefaultCookieHTTPOnly, am.CookieDefaults.HttpOnly) 52 its.Equal(DefaultCookieSecure, am.CookieDefaults.Secure) 53 its.Equal(DefaultCookieSameSiteMode, am.CookieDefaults.SameSite) 54 55 am, err = NewAuthManager(OptAuthManagerCookieDefaults(http.Cookie{ 56 Name: "_FOO_AUTH_", 57 Path: "/admin", 58 HttpOnly: true, 59 Secure: true, 60 SameSite: http.SameSiteLaxMode, 61 })) 62 its.Nil(err) 63 its.Equal("_FOO_AUTH_", am.CookieDefaults.Name) 64 its.Equal("/admin", am.CookieDefaults.Path) 65 its.Equal(true, am.CookieDefaults.HttpOnly) 66 its.Equal(true, am.CookieDefaults.Secure) 67 its.Equal(http.SameSiteLaxMode, am.CookieDefaults.SameSite) 68 69 am, err = NewAuthManager(OptAuthManagerCookieName("X-FOO")) 70 its.Nil(err) 71 its.Equal("X-FOO", am.CookieDefaults.Name) 72 73 am, err = NewAuthManager(OptAuthManagerCookiePath("/foo")) 74 its.Nil(err) 75 its.Equal("/foo", am.CookieDefaults.Path) 76 77 am, err = NewAuthManager(OptAuthManagerCookieHTTPOnly(true)) 78 its.Nil(err) 79 its.Equal(true, am.CookieDefaults.HttpOnly) 80 81 am, err = NewAuthManager(OptAuthManagerCookieSecure(true)) 82 its.Nil(err) 83 its.Equal(true, am.CookieDefaults.Secure) 84 85 am, err = NewAuthManager(OptAuthManagerCookieSameSite(http.SameSiteLaxMode)) 86 its.Nil(err) 87 its.Equal(http.SameSiteLaxMode, am.CookieDefaults.SameSite) 88 89 am, err = NewAuthManager(OptAuthManagerSerializeHandler(func(context.Context, *Session) (string, error) { 90 return "blabla", nil 91 })) 92 its.Nil(err) 93 its.NotNil(am.SerializeHandler) 94 95 am, err = NewAuthManager(OptAuthManagerPersistHandler(func(context.Context, *Session) error { 96 return nil 97 })) 98 its.Nil(err) 99 its.NotNil(am.PersistHandler) 100 101 am, err = NewAuthManager(OptAuthManagerFetchHandler(func(context.Context, string) (*Session, error) { 102 return &Session{SessionID: "blabla"}, nil 103 })) 104 its.Nil(err) 105 its.NotNil(am.FetchHandler) 106 107 am, err = NewAuthManager(OptAuthManagerRemoveHandler(func(context.Context, string) error { 108 return nil 109 })) 110 its.Nil(err) 111 its.NotNil(am.RemoveHandler) 112 113 am, err = NewAuthManager(OptAuthManagerValidateHandler(func(context.Context, *Session) error { 114 return nil 115 })) 116 its.Nil(err) 117 its.NotNil(am.ValidateHandler) 118 119 am, err = NewAuthManager(OptAuthManagerSessionTimeoutProvider(func(*Session) time.Time { 120 return time.Now().UTC() 121 })) 122 its.Nil(err) 123 its.NotNil(am.SessionTimeoutProvider) 124 125 am, err = NewAuthManager(OptAuthManagerLoginRedirectHandler(func(*Ctx) *url.URL { 126 return nil 127 })) 128 its.Nil(err) 129 its.NotNil(am.LoginRedirectHandler) 130 } 131 132 func Test_NewLocalManagerFromCache(t *testing.T) { 133 its := assert.New(t) 134 135 lc := NewLocalSessionCache() 136 am, err := NewLocalAuthManagerFromCache(lc, OptAuthManagerCookieName("X-FOO")) 137 its.Nil(err) 138 its.Equal("X-FOO", am.CookieDefaults.Name) 139 140 am, err = NewLocalAuthManagerFromCache(lc, func(_ *AuthManager) error { return fmt.Errorf("this is just a test") }) 141 its.NotNil(err) 142 } 143 144 func Test_AuthManager_Login(t *testing.T) { 145 its := assert.New(t) 146 147 am, err := NewLocalAuthManager() 148 its.Nil(err) 149 150 sessionExpiresUTC := time.Date(2021, 03, 04, 05, 06, 07, 8, time.UTC) 151 var calledSessionTimeoutProvider bool 152 am.SessionTimeoutProvider = func(session *Session) time.Time { 153 calledSessionTimeoutProvider = true 154 return sessionExpiresUTC 155 } 156 157 var calledPersistHandler bool 158 persistHandler := am.PersistHandler 159 am.PersistHandler = func(ctx context.Context, session *Session) error { 160 calledPersistHandler = true 161 if persistHandler == nil { 162 return nil 163 } 164 return persistHandler(ctx, session) 165 } 166 167 var calledSerializeHandler bool 168 serializeHandler := am.SerializeHandler 169 am.SerializeHandler = func(ctx context.Context, session *Session) (string, error) { 170 calledSerializeHandler = true 171 if serializeHandler == nil { 172 return session.SessionID, nil 173 } 174 return serializeHandler(ctx, session) 175 } 176 177 var calledRemoveHandler bool 178 removeHandler := am.RemoveHandler 179 am.RemoveHandler = func(ctx context.Context, sessionID string) error { 180 calledRemoveHandler = true 181 if removeHandler == nil { 182 return nil 183 } 184 return removeHandler(ctx, sessionID) 185 } 186 187 res := webutil.NewMockResponse(new(bytes.Buffer)) 188 r := NewCtx(res, webutil.NewMockRequest("GET", "/")) 189 190 session, err := am.Login("example-string@blend.com", r) 191 its.Nil(err) 192 its.NotNil(session) 193 its.NotEmpty(session.SessionID) 194 its.NotEmpty(session.RemoteAddr) 195 its.NotEmpty(session.UserAgent) 196 its.Equal("example-string@blend.com", session.UserID) 197 its.False(session.ExpiresUTC.IsZero()) 198 its.Equal(sessionExpiresUTC, session.ExpiresUTC) 199 its.True(calledPersistHandler) 200 its.True(calledSessionTimeoutProvider) 201 its.True(calledSerializeHandler) 202 its.False(calledRemoveHandler) 203 204 cookies := ReadSetCookies(res.Header()) 205 its.NotEmpty(cookies) 206 cookie := cookies[0] 207 its.Equal(am.CookieDefaults.Name, cookie.Name) 208 its.Equal(am.CookieDefaults.Path, cookie.Path) 209 its.Equal(session.SessionID, cookie.Value) 210 } 211 212 func Test_AuthManager_Login_persistError(t *testing.T) { 213 its := assert.New(t) 214 215 am, err := NewLocalAuthManager() 216 its.Nil(err) 217 218 var calledPersistHandler bool 219 am.PersistHandler = func(ctx context.Context, session *Session) error { 220 calledPersistHandler = true 221 return fmt.Errorf("this is just a test") 222 } 223 res := webutil.NewMockResponse(new(bytes.Buffer)) 224 r := NewCtx(res, webutil.NewMockRequest("GET", "/")) 225 226 session, err := am.Login("example-string@blend.com", r) 227 its.NotNil(err) 228 its.True(calledPersistHandler) 229 its.Equal("this is just a test", err.Error()) 230 its.Nil(session) 231 232 cookies := ReadSetCookies(res.Header()) 233 its.Empty(cookies) 234 } 235 236 func Test_AuthManager_Login_serializeError(t *testing.T) { 237 its := assert.New(t) 238 239 am, err := NewLocalAuthManager() 240 its.Nil(err) 241 242 var calledPersistHandler bool 243 persistHandler := am.PersistHandler 244 am.PersistHandler = func(ctx context.Context, session *Session) error { 245 calledPersistHandler = true 246 if persistHandler == nil { 247 return nil 248 } 249 return persistHandler(ctx, session) 250 } 251 252 var calledSerializeHandler bool 253 am.SerializeHandler = func(ctx context.Context, session *Session) (string, error) { 254 calledSerializeHandler = true 255 return "", fmt.Errorf("this is a serialize error") 256 } 257 258 res := webutil.NewMockResponse(new(bytes.Buffer)) 259 r := NewCtx(res, webutil.NewMockRequest("GET", "/")) 260 261 session, err := am.Login("example-string@blend.com", r) 262 its.NotNil(err) 263 its.True(calledPersistHandler) 264 its.True(calledSerializeHandler) 265 its.Equal("this is a serialize error", err.Error()) 266 its.Nil(session) 267 268 cookies := ReadSetCookies(res.Header()) 269 its.Empty(cookies) 270 } 271 272 func Test_AuthManager_Logout(t *testing.T) { 273 its := assert.New(t) 274 275 am, err := NewLocalAuthManager() 276 its.Nil(err) 277 278 var calledRemoveHandler bool 279 removeHandler := am.RemoveHandler 280 am.RemoveHandler = func(ctx context.Context, sessionID string) error { 281 calledRemoveHandler = true 282 if removeHandler == nil { 283 return nil 284 } 285 return removeHandler(ctx, sessionID) 286 } 287 288 res := webutil.NewMockResponse(new(bytes.Buffer)) 289 r := NewCtx(res, webutil.NewMockRequest("GET", "/")) 290 291 session, err := am.Login("example-string@blend.com", r) 292 its.Nil(err) 293 its.NotNil(session) 294 295 res = webutil.NewMockResponse(new(bytes.Buffer)) 296 r = NewCtx(res, webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, session.SessionID)) 297 298 its.Nil(am.Logout(r)) 299 its.True(calledRemoveHandler) 300 301 cookies := ReadSetCookies(res.Header()) 302 its.NotEmpty(cookies) 303 cookie := cookies[0] 304 its.Equal(am.CookieDefaults.Name, cookie.Name) 305 its.Equal(am.CookieDefaults.Path, cookie.Path) 306 its.NotEqual(session.SessionID, cookie.Value, "we should randomize the session cookie on logout") 307 its.True(time.Now().UTC().After(cookie.Expires)) 308 } 309 310 func Test_AuthManager_Logout_sessionValueUnset(t *testing.T) { 311 its := assert.New(t) 312 313 am, err := NewLocalAuthManager() 314 its.Nil(err) 315 316 res := webutil.NewMockResponse(new(bytes.Buffer)) 317 r := NewCtx(res, webutil.NewMockRequest("GET", "/")) 318 its.Nil(am.Logout(r)) 319 320 cookies := ReadSetCookies(res.Header()) 321 its.Empty(cookies) 322 } 323 324 func Test_AuthManager_Logout_removeHandlerUnset(t *testing.T) { 325 its := assert.New(t) 326 327 am, err := NewLocalAuthManager() 328 am.RemoveHandler = nil 329 its.Nil(err) 330 331 res := webutil.NewMockResponse(new(bytes.Buffer)) 332 r := NewCtx(res, webutil.NewMockRequest("GET", "/")) 333 334 session, err := am.Login("example-string@blend.com", r) 335 its.Nil(err) 336 its.NotNil(session) 337 338 res = webutil.NewMockResponse(new(bytes.Buffer)) 339 r = NewCtx(res, webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, session.SessionID)) 340 341 its.Nil(am.Logout(r)) 342 343 cookies := ReadSetCookies(res.Header()) 344 its.NotEmpty(cookies) 345 } 346 347 func Test_AuthManager_VerifyOrExtendSession(t *testing.T) { 348 its := assert.New(t) 349 350 am, err := NewLocalAuthManager() 351 its.Nil(err) 352 353 var calledRestoreHandler bool 354 restoreHandler := am.FetchHandler 355 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 356 calledRestoreHandler = true 357 if restoreHandler == nil { 358 return nil, nil 359 } 360 return restoreHandler(ctx, sessionID) 361 } 362 363 var calledValidateHandler bool 364 validateHandler := am.ValidateHandler 365 am.ValidateHandler = func(ctx context.Context, session *Session) error { 366 calledValidateHandler = true 367 if validateHandler == nil { 368 return nil 369 } 370 return validateHandler(ctx, session) 371 } 372 373 r := NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequest("GET", "/")) 374 session, err := am.Login("example-string@blend.com", r) 375 its.Nil(err) 376 its.NotNil(session) 377 its.False(calledRestoreHandler) 378 its.False(calledValidateHandler) 379 380 r = NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, session.SessionID)) 381 session, err = am.VerifyOrExtendSession(r) 382 its.Nil(err) 383 its.NotNil(session) 384 its.True(calledRestoreHandler) 385 its.True(calledValidateHandler) 386 } 387 388 func Test_AuthManager_VerifyOrExtendSession_sessionUnset(t *testing.T) { 389 its := assert.New(t) 390 391 am, err := NewLocalAuthManager() 392 its.Nil(err) 393 394 var calledRestoreHandler bool 395 restoreHandler := am.FetchHandler 396 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 397 calledRestoreHandler = true 398 if restoreHandler == nil { 399 return nil, nil 400 } 401 return restoreHandler(ctx, sessionID) 402 } 403 404 var calledValidateHandler bool 405 validateHandler := am.ValidateHandler 406 am.ValidateHandler = func(ctx context.Context, session *Session) error { 407 calledValidateHandler = true 408 if validateHandler == nil { 409 return nil 410 } 411 return validateHandler(ctx, session) 412 } 413 414 r := NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequest("GET", "/")) 415 session, err := am.VerifyOrExtendSession(r) 416 its.Nil(err) 417 its.Nil(session) 418 its.False(calledRestoreHandler) 419 its.False(calledValidateHandler) 420 } 421 422 func Test_AuthManager_VerifyOrExtendSession_fetchHandlerUnset(t *testing.T) { 423 its := assert.New(t) 424 425 am, err := NewLocalAuthManager() 426 its.Nil(err) 427 428 r := NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequest("GET", "/")) 429 430 session, err := am.VerifyOrExtendSession(r) 431 its.Nil(err) 432 its.Nil(session) 433 } 434 435 func Test_AuthManager_VerifyOrExtendSession_fetchErrSessionInvalid(t *testing.T) { 436 its := assert.New(t) 437 438 am, err := NewLocalAuthManager() 439 its.Nil(err) 440 441 var calledFetchHandler bool 442 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 443 calledFetchHandler = true 444 return nil, ErrSessionIDEmpty 445 } 446 447 var calledValidateHandler bool 448 validateHandler := am.ValidateHandler 449 am.ValidateHandler = func(ctx context.Context, session *Session) error { 450 calledValidateHandler = true 451 return validateHandler(ctx, session) 452 } 453 454 r := NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequest("GET", "/")) 455 session, err := am.Login("example-string@blend.com", r) 456 its.Nil(err) 457 its.NotNil(session) 458 its.False(calledFetchHandler) 459 its.False(calledValidateHandler) 460 461 r = NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, session.SessionID)) 462 session, err = am.VerifyOrExtendSession(r) 463 its.NotNil(err) 464 its.Equal(ErrSessionIDEmpty, err) 465 its.Nil(session) 466 its.True(calledFetchHandler) 467 its.False(calledValidateHandler) 468 469 cookies := ReadSetCookies(r.Response.Header()) 470 its.NotEmpty(cookies) 471 cookie := cookies[0] 472 its.Equal(am.CookieDefaults.Name, cookie.Name) 473 its.Equal(am.CookieDefaults.Path, cookie.Path) 474 its.True(time.Now().UTC().After(cookie.Expires)) 475 } 476 477 func Test_AuthManager_VerifyOrExtendSession_sessionExpired(t *testing.T) { 478 its := assert.New(t) 479 480 am, err := NewLocalAuthManager() 481 its.Nil(err) 482 483 am.SessionTimeoutProvider = nil 484 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 485 return &Session{UserID: uuid.V4().String(), SessionID: sessionID, ExpiresUTC: time.Now().UTC().Add(-time.Hour)}, nil 486 } 487 488 var calledValidateHandler bool 489 am.ValidateHandler = func(ctx context.Context, session *Session) error { 490 calledValidateHandler = true 491 return nil 492 } 493 494 res := webutil.NewMockResponse(new(bytes.Buffer)) 495 r := NewCtx(res, webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, NewSessionID())) 496 session, err := am.VerifyOrExtendSession(r) 497 its.Nil(err) 498 its.Nil(session) 499 its.False(calledValidateHandler) 500 501 // assert the cookie is expired ... 502 cookies := ReadSetCookies(res.Header()) 503 its.NotEmpty(cookies) 504 505 cookie := cookies[0] 506 its.Equal(am.CookieDefaults.Name, cookie.Name) 507 its.Equal(am.CookieDefaults.Path, cookie.Path) 508 its.True(cookie.Expires.Before(time.Now().UTC()), "the cookie should be expired") 509 } 510 511 func Test_AuthManager_VerifyOrExtendSession_sessionExpired_nil(t *testing.T) { 512 its := assert.New(t) 513 514 am, err := NewLocalAuthManager() 515 its.Nil(err) 516 517 am.SessionTimeoutProvider = nil 518 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 519 return nil, nil 520 } 521 522 var calledValidateHandler bool 523 am.ValidateHandler = func(ctx context.Context, session *Session) error { 524 calledValidateHandler = true 525 return nil 526 } 527 528 res := webutil.NewMockResponse(new(bytes.Buffer)) 529 r := NewCtx(res, webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, NewSessionID())) 530 session, err := am.VerifyOrExtendSession(r) 531 its.Nil(err) 532 its.Nil(session) 533 its.False(calledValidateHandler) 534 535 // assert the cookie is expired ... 536 cookies := ReadSetCookies(res.Header()) 537 its.NotEmpty(cookies) 538 539 cookie := cookies[0] 540 its.Equal(am.CookieDefaults.Name, cookie.Name) 541 its.Equal(am.CookieDefaults.Path, cookie.Path) 542 its.True(cookie.Expires.Before(time.Now().UTC()), "the cookie should be expired") 543 } 544 545 func Test_AuthManager_VerifyOrExtendSession_sessionExpired_zero(t *testing.T) { 546 its := assert.New(t) 547 548 am, err := NewLocalAuthManager() 549 its.Nil(err) 550 551 am.SessionTimeoutProvider = nil 552 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 553 return &Session{}, nil 554 } 555 556 var calledValidateHandler bool 557 am.ValidateHandler = func(ctx context.Context, session *Session) error { 558 calledValidateHandler = true 559 return nil 560 } 561 562 res := webutil.NewMockResponse(new(bytes.Buffer)) 563 r := NewCtx(res, webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, NewSessionID())) 564 session, err := am.VerifyOrExtendSession(r) 565 its.Nil(err) 566 its.Nil(session) 567 its.False(calledValidateHandler) 568 569 // assert the cookie is expired ... 570 cookies := ReadSetCookies(res.Header()) 571 its.NotEmpty(cookies) 572 573 cookie := cookies[0] 574 its.Equal(am.CookieDefaults.Name, cookie.Name) 575 its.Equal(am.CookieDefaults.Path, cookie.Path) 576 its.True(cookie.Expires.Before(time.Now().UTC()), "the cookie should be expired") 577 } 578 579 func Test_AuthManager_VerifyOrExtendSession_failsValidation(t *testing.T) { 580 its := assert.New(t) 581 582 am, err := NewLocalAuthManager() 583 its.Nil(err) 584 585 var calledFetchHandler bool 586 fetchHandler := am.FetchHandler 587 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 588 calledFetchHandler = true 589 return fetchHandler(ctx, sessionID) 590 } 591 592 var calledValidateHandler bool 593 am.ValidateHandler = func(ctx context.Context, session *Session) error { 594 calledValidateHandler = true 595 return fmt.Errorf("this is just a test") 596 } 597 598 r := NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequest("GET", "/")) 599 session, err := am.Login("example-string@blend.com", r) 600 its.Nil(err) 601 its.NotNil(session) 602 its.False(calledFetchHandler) 603 its.False(calledValidateHandler) 604 605 r = NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, session.SessionID)) 606 session, err = am.VerifyOrExtendSession(r) 607 its.NotNil(err) 608 its.Nil(session) 609 its.True(calledFetchHandler) 610 its.True(calledValidateHandler) 611 612 // assert the cookie is expired ... 613 cookies := ReadSetCookies(r.Response.Header()) 614 // for now, we should not expire the cookie on a validatin failure 615 its.Empty(cookies) 616 } 617 618 func Test_AuthManager_VerifyOrExtendSession_sessionTimeout_unchanged(t *testing.T) { 619 its := assert.New(t) 620 621 am, err := NewLocalAuthManager() 622 its.Nil(err) 623 624 var calledPersistHandler bool 625 persistHandler := am.PersistHandler 626 am.PersistHandler = func(ctx context.Context, session *Session) error { 627 calledPersistHandler = true 628 return persistHandler(ctx, session) 629 } 630 631 var calledFetchHandler bool 632 fetchHandler := am.FetchHandler 633 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 634 calledFetchHandler = true 635 return fetchHandler(ctx, sessionID) 636 } 637 638 var calledValidateHandler bool 639 am.ValidateHandler = func(ctx context.Context, session *Session) error { 640 calledValidateHandler = true 641 return nil 642 } 643 var calledSessionTimeoutProvider bool 644 am.SessionTimeoutProvider = func(session *Session) time.Time { 645 calledSessionTimeoutProvider = true 646 return session.ExpiresUTC 647 } 648 649 r := NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequest("GET", "/")) 650 session, err := am.Login("example-string@blend.com", r) 651 its.Nil(err) 652 its.NotNil(session) 653 its.False(calledFetchHandler) 654 its.False(calledValidateHandler) 655 its.True(calledPersistHandler) 656 its.True(calledSessionTimeoutProvider) 657 658 calledPersistHandler = false 659 calledSessionTimeoutProvider = false 660 661 r = NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, session.SessionID)) 662 session, err = am.VerifyOrExtendSession(r) 663 its.Nil(err) 664 its.NotNil(session) 665 its.True(calledFetchHandler) 666 its.True(calledValidateHandler) 667 its.False(calledPersistHandler) 668 its.True(calledSessionTimeoutProvider) 669 } 670 671 func Test_AuthManager_VerifyOrExtendSession_sessionTimeout_changed(t *testing.T) { 672 its := assert.New(t) 673 674 am, err := NewLocalAuthManager() 675 its.Nil(err) 676 677 var calledFetchHandler bool 678 fetchHandler := am.FetchHandler 679 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 680 calledFetchHandler = true 681 return fetchHandler(ctx, sessionID) 682 } 683 684 var calledValidateHandler bool 685 am.ValidateHandler = func(ctx context.Context, session *Session) error { 686 calledValidateHandler = true 687 return nil 688 } 689 690 r := NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequest("GET", "/")) 691 session, err := am.Login("example-string@blend.com", r) 692 its.Nil(err) 693 its.NotNil(session) 694 its.False(calledFetchHandler) 695 its.False(calledValidateHandler) 696 697 var calledPersistHandler bool 698 persistHandler := am.PersistHandler 699 am.PersistHandler = func(ctx context.Context, session *Session) error { 700 calledPersistHandler = true 701 return persistHandler(ctx, session) 702 } 703 704 expiresUTC := time.Now().UTC() 705 var calledSessionTimeoutProvider bool 706 am.SessionTimeoutProvider = func(session *Session) time.Time { 707 calledSessionTimeoutProvider = true 708 return expiresUTC 709 } 710 711 r = NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, session.SessionID)) 712 session, err = am.VerifyOrExtendSession(r) 713 its.Nil(err) 714 its.NotNil(session) 715 its.True(calledFetchHandler) 716 its.True(calledValidateHandler) 717 its.True(calledPersistHandler) 718 its.True(calledSessionTimeoutProvider) 719 720 // assert the cookie is expired ... 721 cookies := ReadSetCookies(r.Response.Header()) 722 its.NotEmpty(cookies) 723 724 cookie := cookies[0] 725 its.Equal(am.CookieDefaults.Name, cookie.Name) 726 its.Equal(am.CookieDefaults.Path, cookie.Path) 727 its.InTimeDelta(expiresUTC, cookie.Expires, time.Second, "the cookie have an expiration") 728 } 729 730 func Test_AuthManager_VerifyOrExpireSession_sessionTimeout_persistError(t *testing.T) { 731 its := assert.New(t) 732 733 am, err := NewLocalAuthManager() 734 its.Nil(err) 735 736 var calledFetchHandler bool 737 fetchHandler := am.FetchHandler 738 am.FetchHandler = func(ctx context.Context, sessionID string) (*Session, error) { 739 calledFetchHandler = true 740 return fetchHandler(ctx, sessionID) 741 } 742 743 var calledValidateHandler bool 744 am.ValidateHandler = func(ctx context.Context, session *Session) error { 745 calledValidateHandler = true 746 return nil 747 } 748 749 r := NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequest("GET", "/")) 750 session, err := am.Login("example-string@blend.com", r) 751 its.Nil(err) 752 its.NotNil(session) 753 its.False(calledFetchHandler) 754 its.False(calledValidateHandler) 755 756 var calledPersistHandler bool 757 am.PersistHandler = func(ctx context.Context, session *Session) error { 758 calledPersistHandler = true 759 return fmt.Errorf("this is just a test") 760 } 761 762 expiresUTC := time.Now().UTC() 763 var calledSessionTimeoutProvider bool 764 am.SessionTimeoutProvider = func(session *Session) time.Time { 765 calledSessionTimeoutProvider = true 766 return expiresUTC 767 } 768 769 r = NewCtx(webutil.NewMockResponse(new(bytes.Buffer)), webutil.NewMockRequestWithCookie("GET", "/", am.CookieDefaults.Name, session.SessionID)) 770 session, err = am.VerifyOrExtendSession(r) 771 its.NotNil(err) 772 its.Equal("this is just a test", err.Error()) 773 its.Nil(session) 774 its.True(calledFetchHandler) 775 its.True(calledValidateHandler) 776 its.True(calledSessionTimeoutProvider) 777 its.True(calledPersistHandler) 778 779 // assert the cookie is expired ... 780 cookies := ReadSetCookies(r.Response.Header()) 781 its.Empty(cookies) 782 } 783 784 func Test_AuthManager_LoginRedirect_loginRedirectHandlerUnset(t *testing.T) { 785 its := assert.New(t) 786 787 am := AuthManager{} 788 ctx := MockCtx(http.MethodGet, "/api/foo/bar", OptCtxDefaultProvider(Text)) 789 res := am.LoginRedirect(ctx) 790 its.NotNil(res) 791 typed, ok := res.(*RawResult) 792 its.True(ok) 793 its.Equal(http.StatusUnauthorized, typed.StatusCode) 794 } 795 796 func Test_AuthManager_LoginRedirect_loginRedirectHandler(t *testing.T) { 797 its := assert.New(t) 798 799 am := AuthManager{ 800 LoginRedirectHandler: func(r *Ctx) *url.URL { 801 return &url.URL{ 802 Path: "/foo", 803 } 804 }, 805 } 806 ctx := MockCtx(http.MethodGet, "/api/foo/bar", OptCtxDefaultProvider(Text)) 807 res := am.LoginRedirect(ctx) 808 its.NotNil(res) 809 typed, ok := res.(*RedirectResult) 810 its.True(ok) 811 its.Empty(typed.Method) 812 its.Equal("/foo", typed.RedirectURI) 813 } 814 815 func Test_AuthManager_expire(t *testing.T) { 816 its := assert.New(t) 817 818 expectedSessionID := uuid.V4().String() 819 cookieName := "my-auth-cookie" 820 821 var didCallRemoveHandler, sessionIDCorrect bool 822 am := AuthManager{ 823 CookieDefaults: http.Cookie{ 824 Name: cookieName, 825 }, 826 RemoveHandler: func(c context.Context, sessionID string) error { 827 didCallRemoveHandler = true 828 sessionIDCorrect = sessionID == expectedSessionID 829 return nil 830 }, 831 } 832 ctx := MockCtx(http.MethodGet, "/api/foo/bar", OptCtxDefaultProvider(Text), OptCtxCookieValue(cookieName, expectedSessionID)) 833 err := am.expire(ctx, expectedSessionID) 834 its.Nil(err) 835 its.True(didCallRemoveHandler) 836 its.True(sessionIDCorrect) 837 838 // assert the cookie is expired ... 839 cookies := ReadSetCookies(ctx.Response.Header()) 840 its.NotEmpty(cookies) 841 842 cookie := cookies[0] 843 its.Equal(am.CookieDefaults.Name, cookie.Name) 844 its.Equal(am.CookieDefaults.Path, cookie.Path) 845 its.True(cookie.Expires.Before(time.Now().UTC()), "the cookie should be expired") 846 } 847 848 func Test_AuthManager_expire_removeError(t *testing.T) { 849 its := assert.New(t) 850 851 var didCallRemoveHandler bool 852 am := AuthManager{ 853 RemoveHandler: func(c context.Context, sessionID string) error { 854 didCallRemoveHandler = true 855 return fmt.Errorf("this is just a test") 856 }, 857 } 858 ctx := MockCtx(http.MethodGet, "/api/foo/bar", OptCtxDefaultProvider(Text)) 859 err := am.expire(ctx, uuid.V4().String()) 860 its.NotNil(err) 861 its.Equal("this is just a test", err.Error()) 862 its.True(didCallRemoveHandler) 863 864 // assert the cookie is expired ... 865 cookies := ReadSetCookies(ctx.Response.Header()) 866 its.Empty(cookies) 867 }