1
0
mirror of https://github.com/volatiletech/authboss.git synced 2024-12-12 10:45:11 +02:00
authboss/router_test.go
Aaron L 704697472f Add redirection on pages when logged in.
- Stop logged in users from accessing pages like auth/recover etc.
- Ensure that half-authed users are allowed access to auth-like pages.
- Make sure that if users have a remember token, it's processed before
  we decide if a user is logged in or not, preventing or granting access
  to these pages.
- Fix #58
2015-04-10 22:41:59 -07:00

303 lines
7.6 KiB
Go

package authboss
import (
"bytes"
"errors"
"io/ioutil"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
const testRouterModName = "testrouter"
func init() {
RegisterModule(testRouterModName, testRouterModule{})
}
type testRouterModule struct {
routes RouteTable
}
func (t testRouterModule) Initialize(ab *Authboss) error { return nil }
func (t testRouterModule) Routes() RouteTable { return t.routes }
func (t testRouterModule) Storage() StorageOptions { return nil }
func testRouterSetup() (*Authboss, http.Handler, *bytes.Buffer) {
ab := New()
logger := &bytes.Buffer{}
ab.LogWriter = logger
ab.Init(testRouterModName)
ab.MountPath = "/prefix"
ab.SessionStoreMaker = func(w http.ResponseWriter, r *http.Request) ClientStorer { return mockClientStore{} }
ab.CookieStoreMaker = func(w http.ResponseWriter, r *http.Request) ClientStorer { return mockClientStore{} }
logger.Reset() // Clear out the module load messages
return ab, ab.NewRouter(), logger
}
// testRouterCallbackSetup is NOT safe for use by multiple goroutines, don't use parallel
func testRouterCallbackSetup(path string, h HandlerFunc) (w *httptest.ResponseRecorder, r *http.Request) {
registeredModules[testRouterModName] = testRouterModule{
routes: map[string]HandlerFunc{path: h},
}
w = httptest.NewRecorder()
r, _ = http.NewRequest("GET", "http://localhost/prefix"+path, nil)
return w, r
}
func TestRouter(t *testing.T) {
called := false
w, r := testRouterCallbackSetup("/called", func(ctx *Context, w http.ResponseWriter, r *http.Request) error {
called = true
return nil
})
_, router, _ := testRouterSetup()
router.ServeHTTP(w, r)
if !called {
t.Error("Expected handler to be called.")
}
}
func TestRouter_NotFound(t *testing.T) {
ab, router, _ := testRouterSetup()
w := httptest.NewRecorder()
r, _ := http.NewRequest("GET", "http://localhost/wat", nil)
router.ServeHTTP(w, r)
if w.Code != http.StatusNotFound {
t.Error("Wrong code:", w.Code)
}
if body := w.Body.String(); body != "404 Page not found" {
t.Error("Wrong body:", body)
}
called := false
ab.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
called = true
})
router.ServeHTTP(w, r)
if !called {
t.Error("Should be called.")
}
}
func TestRouter_BadRequest(t *testing.T) {
err := ClientDataErr{"what"}
w, r := testRouterCallbackSetup("/badrequest",
func(ctx *Context, w http.ResponseWriter, r *http.Request) error {
return err
},
)
ab, router, logger := testRouterSetup()
logger.Reset()
router.ServeHTTP(w, r)
if w.Code != http.StatusBadRequest {
t.Error("Wrong code:", w.Code)
}
if body := w.Body.String(); body != "400 Bad request" {
t.Error("Wrong body:", body)
}
if str := logger.String(); !strings.Contains(str, err.Error()) {
t.Error(str)
}
called := false
ab.BadRequestHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
called = true
})
logger.Reset()
router.ServeHTTP(w, r)
if !called {
t.Error("Should be called.")
}
if str := logger.String(); !strings.Contains(str, err.Error()) {
t.Error(str)
}
}
func TestRouter_Error(t *testing.T) {
err := errors.New("error")
w, r := testRouterCallbackSetup("/error",
func(ctx *Context, w http.ResponseWriter, r *http.Request) error {
return err
},
)
ab, router, logger := testRouterSetup()
logger.Reset()
router.ServeHTTP(w, r)
if w.Code != http.StatusInternalServerError {
t.Error("Wrong code:", w.Code)
}
if body := w.Body.String(); body != "500 An error has occurred" {
t.Error("Wrong body:", body)
}
if str := logger.String(); !strings.Contains(str, err.Error()) {
t.Error(str)
}
called := false
ab.ErrorHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
called = true
})
logger.Reset()
router.ServeHTTP(w, r)
if !called {
t.Error("Should be called.")
}
if str := logger.String(); !strings.Contains(str, err.Error()) {
t.Error(str)
}
}
func TestRouter_Redirect(t *testing.T) {
err := ErrAndRedirect{
Err: errors.New("error"),
Location: "/",
FlashSuccess: "yay",
FlashError: "nay",
}
w, r := testRouterCallbackSetup("/error",
func(ctx *Context, w http.ResponseWriter, r *http.Request) error {
return err
},
)
ab, router, logger := testRouterSetup()
session := mockClientStore{}
ab.SessionStoreMaker = func(w http.ResponseWriter, r *http.Request) ClientStorer { return session }
logger.Reset()
router.ServeHTTP(w, r)
if w.Code != http.StatusFound {
t.Error("Wrong code:", w.Code)
}
if loc := w.Header().Get("Location"); loc != err.Location {
t.Error("Wrong location:", loc)
}
if succ, ok := session.Get(FlashSuccessKey); !ok || succ != err.FlashSuccess {
t.Error(succ, ok)
}
if fail, ok := session.Get(FlashErrorKey); !ok || fail != err.FlashError {
t.Error(fail, ok)
}
}
func TestRouter_redirectIfLoggedIn(t *testing.T) {
t.Parallel()
tests := []struct {
Path string
LoggedIn bool
HalfAuthed bool
ShouldRedirect bool
}{
// These routes will be accessed depending on logged in and half auth's value
{"/auth", false, false, false},
{"/auth", true, false, true},
{"/auth", true, true, false},
{"/oauth2/facebook", false, false, false},
{"/oauth2/facebook", true, false, true},
{"/oauth2/facebook", true, true, false},
{"/oauth2/callback/facebook", false, false, false},
{"/oauth2/callback/facebook", true, false, true},
{"/oauth2/callback/facebook", true, true, false},
// These are logout routes and never redirect
{"/logout", true, false, false},
{"/logout", true, true, false},
{"/oauth2/logout", true, false, false},
{"/oauth2/logout", true, true, false},
// These routes should always redirect despite half auth
{"/register", true, true, true},
{"/recover", true, true, true},
{"/register", false, false, false},
{"/recover", false, false, false},
}
storer := mockStorer{"john@john.com": Attributes{
StoreEmail: "john@john.com",
StorePassword: "password",
}}
ab := New()
ab.Storer = storer
for i, test := range tests {
session := mockClientStore{}
cookies := mockClientStore{}
ctx := ab.NewContext()
ctx.SessionStorer = session
ctx.CookieStorer = cookies
if test.LoggedIn {
session[SessionKey] = "john@john.com"
}
if test.HalfAuthed {
session[SessionHalfAuthKey] = "true"
}
r, _ := http.NewRequest("GET", test.Path, nil)
w := httptest.NewRecorder()
handled := redirectIfLoggedIn(ctx, w, r)
if test.ShouldRedirect && (!handled || w.Code != http.StatusFound) {
t.Errorf("%d) It should have redirected the request: %q %t %d", i, test.Path, handled, w.Code)
} else if !test.ShouldRedirect && (handled || w.Code != http.StatusOK) {
t.Errorf("%d) It should have NOT redirected the request: %q %t %d", i, test.Path, handled, w.Code)
}
}
}
type deathStorer struct{}
func (d deathStorer) Create(key string, attributes Attributes) error { return nil }
func (d deathStorer) Put(key string, attributes Attributes) error { return nil }
func (d deathStorer) Get(key string) (interface{}, error) { return nil, errors.New("explosion") }
func TestRouter_redirectIfLoggedInError(t *testing.T) {
t.Parallel()
ab := New()
ab.LogWriter = ioutil.Discard
ab.Storer = deathStorer{}
session := mockClientStore{SessionKey: "john"}
cookies := mockClientStore{}
ctx := ab.NewContext()
ctx.SessionStorer = session
ctx.CookieStorer = cookies
r, _ := http.NewRequest("GET", "/auth", nil)
w := httptest.NewRecorder()
handled := redirectIfLoggedIn(ctx, w, r)
if !handled {
t.Error("It should have been handled.")
}
if w.Code != http.StatusInternalServerError {
t.Error("It should have internal server error'd:", w.Code)
}
}