1
0
mirror of https://github.com/volatiletech/authboss.git synced 2024-12-12 10:45:11 +02:00
authboss/auth/auth_test.go

380 lines
10 KiB
Go
Raw Normal View History

package auth
2015-02-25 01:01:56 +02:00
import (
"bytes"
2015-02-25 01:01:56 +02:00
"errors"
"html/template"
"io/ioutil"
2015-02-25 01:01:56 +02:00
"net/http"
"net/http/httptest"
"strings"
"testing"
"gopkg.in/authboss.v0"
"gopkg.in/authboss.v0/internal/mocks"
)
func testSetup() (a *Auth, s *mocks.MockStorer) {
s = mocks.NewMockStorer()
2015-04-01 00:27:47 +02:00
ab := authboss.New()
ab.LogWriter = ioutil.Discard
ab.Layout = template.Must(template.New("").Parse(`{{template "authboss" .}}`))
ab.Storer = s
ab.XSRFName = "xsrf"
ab.XSRFMaker = func(_ http.ResponseWriter, _ *http.Request) string {
2015-02-25 01:01:56 +02:00
return "xsrfvalue"
}
2015-04-01 00:27:47 +02:00
ab.PrimaryID = authboss.StoreUsername
2015-02-25 01:01:56 +02:00
a = &Auth{}
2015-04-01 00:27:47 +02:00
if err := a.Initialize(ab); err != nil {
2015-02-25 01:01:56 +02:00
panic(err)
}
return a, s
}
2015-04-01 00:27:47 +02:00
func testRequest(ab *authboss.Authboss, method string, postFormValues ...string) (*authboss.Context, *httptest.ResponseRecorder, *http.Request, authboss.ClientStorerErr) {
2015-02-25 01:01:56 +02:00
sessionStorer := mocks.NewMockClientStorer()
ctx := ab.NewContext()
r := mocks.MockRequest(method, postFormValues...)
2015-02-25 01:01:56 +02:00
ctx.SessionStorer = sessionStorer
return ctx, httptest.NewRecorder(), r, sessionStorer
}
func TestAuth(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
2015-02-25 01:01:56 +02:00
a, _ := testSetup()
storage := a.Storage()
2015-04-01 00:27:47 +02:00
if storage[a.PrimaryID] != authboss.String {
t.Error("Expected storage KV:", a.PrimaryID, authboss.String)
2015-02-25 01:01:56 +02:00
}
if storage[authboss.StorePassword] != authboss.String {
t.Error("Expected storage KV:", authboss.StorePassword, authboss.String)
}
routes := a.Routes()
if routes["/login"] == nil {
t.Error("Expected route '/login' with handleFunc")
2015-02-25 01:01:56 +02:00
}
if routes["/logout"] == nil {
t.Error("Expected route '/logout' with handleFunc")
2015-02-25 01:01:56 +02:00
}
}
func TestAuth_loginHandlerFunc_GET(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
2015-02-25 01:01:56 +02:00
a, _ := testSetup()
2015-04-01 00:27:47 +02:00
ctx, w, r, _ := testRequest(a.Authboss, "GET")
2015-02-25 01:01:56 +02:00
if err := a.loginHandlerFunc(ctx, w, r); err != nil {
t.Error("Unexpected error:", err)
}
if w.Code != http.StatusOK {
t.Error("Unexpected status:", w.Code)
}
body := w.Body.String()
if !strings.Contains(body, "<form") {
t.Error("Should have rendered a form")
}
2015-04-01 00:27:47 +02:00
if !strings.Contains(body, `name="`+a.PrimaryID) {
2015-02-25 01:01:56 +02:00
t.Error("Form should contain the primary ID field:", body)
}
if !strings.Contains(body, `name="password"`) {
t.Error("Form should contain password field:", body)
}
}
func TestAuth_loginHandlerFunc_POST_ReturnsErrorOnCallbackFailure(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
a, storer := testSetup()
storer.Users["john"] = authboss.Attributes{"password": "$2a$10$B7aydtqVF9V8RSNx3lCKB.l09jqLV/aMiVqQHajtL7sWGhCS9jlOu"}
2015-02-25 01:01:56 +02:00
2015-04-01 00:27:47 +02:00
a.Callbacks = authboss.NewCallbacks()
a.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
2015-02-25 01:01:56 +02:00
return authboss.InterruptNone, errors.New("explode")
})
2015-04-01 00:27:47 +02:00
ctx, w, r, _ := testRequest(a.Authboss, "POST", "username", "john", "password", "1234")
2015-02-25 01:01:56 +02:00
if err := a.loginHandlerFunc(ctx, w, r); err.Error() != "explode" {
t.Error("Unexpected error:", err)
}
}
func TestAuth_loginHandlerFunc_POST_RedirectsWhenInterrupted(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
a, storer := testSetup()
storer.Users["john"] = authboss.Attributes{"password": "$2a$10$B7aydtqVF9V8RSNx3lCKB.l09jqLV/aMiVqQHajtL7sWGhCS9jlOu"}
2015-02-25 01:01:56 +02:00
2015-04-01 00:27:47 +02:00
a.Callbacks = authboss.NewCallbacks()
a.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
2015-02-25 01:01:56 +02:00
return authboss.InterruptAccountLocked, nil
})
2015-04-01 00:27:47 +02:00
ctx, w, r, sessionStore := testRequest(a.Authboss, "POST", "username", "john", "password", "1234")
2015-02-25 01:01:56 +02:00
if err := a.loginHandlerFunc(ctx, w, r); err != nil {
t.Error("Unexpected error:", err)
}
if w.Code != http.StatusFound {
t.Error("Unexpected status:", w.Code)
}
loc := w.Header().Get("Location")
2015-04-01 00:27:47 +02:00
if loc != a.AuthLoginFailPath {
2015-02-25 01:01:56 +02:00
t.Error("Unexpeced location:", loc)
}
expectedMsg := "Your account has been locked."
if msg, ok := sessionStore.Get(authboss.FlashErrorKey); !ok || msg != expectedMsg {
t.Error("Expected error flash message:", expectedMsg)
}
2015-04-01 00:27:47 +02:00
a.Callbacks = authboss.NewCallbacks()
a.Callbacks.Before(authboss.EventAuth, func(_ *authboss.Context) (authboss.Interrupt, error) {
2015-02-25 01:01:56 +02:00
return authboss.InterruptAccountNotConfirmed, nil
})
if err := a.loginHandlerFunc(ctx, w, r); err != nil {
t.Error("Unexpected error:", err)
}
if w.Code != http.StatusFound {
t.Error("Unexpected status:", w.Code)
}
loc = w.Header().Get("Location")
2015-04-01 00:27:47 +02:00
if loc != a.AuthLoginFailPath {
2015-02-25 01:01:56 +02:00
t.Error("Unexpeced location:", loc)
}
expectedMsg = "Your account has not been confirmed."
if msg, ok := sessionStore.Get(authboss.FlashErrorKey); !ok || msg != expectedMsg {
t.Error("Expected error flash message:", expectedMsg)
}
}
func TestAuth_loginHandlerFunc_POST_AuthenticationFailure(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
2015-02-25 01:01:56 +02:00
a, _ := testSetup()
log := &bytes.Buffer{}
a.LogWriter = log
2015-04-01 00:27:47 +02:00
ctx, w, r, _ := testRequest(a.Authboss, "POST", "username", "john", "password", "1")
2015-02-25 01:01:56 +02:00
if err := a.loginHandlerFunc(ctx, w, r); err != nil {
t.Error("Unexpected error:", err)
}
if w.Code != http.StatusOK {
t.Error("Unexpected status:", w.Code)
}
body := w.Body.String()
if !strings.Contains(body, "invalid username and/or password") {
t.Error("Should have rendered with error")
}
2015-04-01 00:27:47 +02:00
ctx, w, r, _ = testRequest(a.Authboss, "POST", "username", "john", "password", "1234")
2015-02-25 01:01:56 +02:00
if err := a.loginHandlerFunc(ctx, w, r); err != nil {
t.Error("Unexpected error:", err)
}
if w.Code != http.StatusOK {
t.Error("Unexpected status:", w.Code)
}
body = w.Body.String()
if !strings.Contains(body, "invalid username and/or password") {
2015-02-25 01:01:56 +02:00
t.Error("Should have rendered with error")
}
ctx, w, r, _ = testRequest(a.Authboss, "POST", "username", "jake", "password", "1")
if err := a.loginHandlerFunc(ctx, w, r); err != nil {
t.Error("Unexpected error:", err)
}
if w.Code != http.StatusOK {
t.Error("Unexpected status:", w.Code)
}
body = w.Body.String()
if !strings.Contains(body, "invalid username and/or password") {
t.Error("Should have rendered with error")
}
2015-02-25 01:01:56 +02:00
}
func TestAuth_loginHandlerFunc_POST(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
2015-02-25 01:01:56 +02:00
a, storer := testSetup()
storer.Users["john"] = authboss.Attributes{"password": "$2a$10$B7aydtqVF9V8RSNx3lCKB.l09jqLV/aMiVqQHajtL7sWGhCS9jlOu"}
2015-04-01 00:27:47 +02:00
ctx, w, r, _ := testRequest(a.Authboss, "POST", "username", "john", "password", "1234")
2015-02-25 01:01:56 +02:00
cb := mocks.NewMockAfterCallback()
2015-04-01 00:27:47 +02:00
a.Callbacks = authboss.NewCallbacks()
a.Callbacks.After(authboss.EventAuth, cb.Fn)
a.AuthLoginOKPath = "/dashboard"
sessions := mocks.NewMockClientStorer()
ctx.SessionStorer = sessions
2015-02-25 01:01:56 +02:00
if err := a.loginHandlerFunc(ctx, w, r); err != nil {
t.Error("Unexpected error:", err)
}
if _, ok := ctx.Values[authboss.CookieRemember]; !ok {
t.Error("Authboss cookie remember should be set for the callback")
}
2015-02-25 01:01:56 +02:00
if !cb.HasBeenCalled {
t.Error("Expected after callback to have been called")
}
if w.Code != http.StatusFound {
t.Error("Unexpected status:", w.Code)
}
loc := w.Header().Get("Location")
2015-04-01 00:27:47 +02:00
if loc != a.AuthLoginOKPath {
2015-02-25 01:01:56 +02:00
t.Error("Unexpeced location:", loc)
}
val, ok := sessions.Values[authboss.SessionKey]
if !ok {
t.Error("Expected session to be set")
} else if val != "john" {
t.Error("Expected session value to be authed username")
}
2015-02-25 01:01:56 +02:00
}
func TestAuth_loginHandlerFunc_OtherMethods(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
2015-02-25 01:01:56 +02:00
a, _ := testSetup()
methods := []string{"HEAD", "PUT", "DELETE", "TRACE", "CONNECT"}
for i, method := range methods {
r, err := http.NewRequest(method, "/login", nil)
if err != nil {
t.Errorf("%d> Unexpected error '%s'", i, err)
}
w := httptest.NewRecorder()
if err := a.loginHandlerFunc(nil, w, r); err != nil {
t.Errorf("%d> Unexpected error: %s", i, err)
}
if http.StatusMethodNotAllowed != w.Code {
t.Errorf("%d> Expected status code %d, got %d", i, http.StatusMethodNotAllowed, w.Code)
continue
}
}
}
func TestAuth_validateCredentials(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
ab := authboss.New()
2015-02-25 01:01:56 +02:00
storer := mocks.NewMockStorer()
2015-04-01 00:27:47 +02:00
ab.Storer = storer
2015-02-25 01:01:56 +02:00
2015-04-01 00:27:47 +02:00
ctx := ab.NewContext()
storer.Users["john"] = authboss.Attributes{"password": "$2a$10$pgFsuQwdhwOdZp/v52dvHeEi53ZaI7dGmtwK4bAzGGN5A4nT6doqm"}
if _, err := validateCredentials(ctx, "john", "a"); err != nil {
2015-02-25 01:01:56 +02:00
t.Error("Unexpected error:", err)
}
ctx = ab.NewContext()
if valid, err := validateCredentials(ctx, "jake", "a"); err != nil {
t.Error("Expect no error when user not found:", err)
} else if valid {
t.Error("Expect invalid when not user found")
}
ctx = ab.NewContext()
storer.GetErr = "Failed to load user"
if _, err := validateCredentials(ctx, "", ""); err.Error() != "Failed to load user" {
2015-02-25 01:01:56 +02:00
t.Error("Unexpected error:", err)
}
2015-02-25 01:01:56 +02:00
}
func TestAuth_logoutHandlerFunc_GET(t *testing.T) {
2015-04-01 00:27:47 +02:00
t.Parallel()
2015-02-25 01:01:56 +02:00
a, _ := testSetup()
2015-04-01 00:27:47 +02:00
a.AuthLogoutOKPath = "/dashboard"
2015-02-25 01:01:56 +02:00
2015-04-01 00:27:47 +02:00
ctx, w, r, sessionStorer := testRequest(a.Authboss, "GET")
2015-02-25 01:01:56 +02:00
sessionStorer.Put(authboss.SessionKey, "asdf")
sessionStorer.Put(authboss.SessionLastAction, "1234")
cookieStorer := mocks.NewMockClientStorer(authboss.CookieRemember, "qwert")
ctx.CookieStorer = cookieStorer
2015-02-25 01:01:56 +02:00
if err := a.logoutHandlerFunc(ctx, w, r); err != nil {
t.Error("Unexpected error:", err)
}
if val, ok := sessionStorer.Get(authboss.SessionKey); ok {
2015-03-16 23:51:44 +02:00
t.Error("Unexpected session key:", val)
}
if val, ok := sessionStorer.Get(authboss.SessionLastAction); ok {
2015-03-16 23:51:44 +02:00
t.Error("Unexpected last action:", val)
}
if val, ok := cookieStorer.Get(authboss.CookieRemember); ok {
2015-03-16 23:51:44 +02:00
t.Error("Unexpected rm cookie:", val)
2015-02-25 01:01:56 +02:00
}
if http.StatusFound != w.Code {
t.Errorf("Expected status code %d, got %d", http.StatusFound, w.Code)
}
location := w.Header().Get("Location")
if location != "/dashboard" {
t.Errorf("Expected lcoation %s, got %s", "/dashboard", location)
}
}
func TestAuth_logoutHandlerFunc_OtherMethods(t *testing.T) {
a, _ := testSetup()
methods := []string{"HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT"}
for i, method := range methods {
r, err := http.NewRequest(method, "/logout", nil)
if err != nil {
t.Errorf("%d> Unexpected error '%s'", i, err)
}
w := httptest.NewRecorder()
if err := a.logoutHandlerFunc(nil, w, r); err != nil {
t.Errorf("%d> Unexpected error: %s", i, err)
}
if http.StatusMethodNotAllowed != w.Code {
t.Errorf("%d> Expected status code %d, got %d", i, http.StatusMethodNotAllowed, w.Code)
continue
}
}
}