package auth import ( "errors" "html/template" "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() authboss.Cfg = authboss.NewConfig() authboss.Cfg.Layout = template.Must(template.New("").Parse(`{{template "authboss" .}}`)) authboss.Cfg.Storer = s authboss.Cfg.XSRFName = "xsrf" authboss.Cfg.XSRFMaker = func(_ http.ResponseWriter, _ *http.Request) string { return "xsrfvalue" } authboss.Cfg.PrimaryID = authboss.StoreUsername a = &Auth{} if err := a.Initialize(); err != nil { panic(err) } return a, s } func testRequest(method string, postFormValues ...string) (*authboss.Context, *httptest.ResponseRecorder, *http.Request, authboss.ClientStorerErr) { r, err := http.NewRequest(method, "", nil) if err != nil { panic(err) } sessionStorer := mocks.NewMockClientStorer() ctx := mocks.MockRequestContext(postFormValues...) ctx.SessionStorer = sessionStorer return ctx, httptest.NewRecorder(), r, sessionStorer } func TestAuth(t *testing.T) { a, _ := testSetup() storage := a.Storage() if storage[authboss.Cfg.PrimaryID] != authboss.String { t.Error("Expected storage KV:", authboss.Cfg.PrimaryID, authboss.String) } 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") } if routes["/logout"] == nil { t.Error("Expected route '/logout' with handleFunc") } } func TestAuth_loginHandlerFunc_GET_RedirectsWhenHalfAuthed(t *testing.T) { a, _ := testSetup() ctx, w, r, sessionStore := testRequest("GET") sessionStore.Put(authboss.SessionKey, "a") sessionStore.Put(authboss.SessionHalfAuthKey, "false") authboss.Cfg.AuthLoginSuccessRoute = "/dashboard" if err := a.loginHandlerFunc(ctx, w, r); err != nil { t.Error("Unexpeced error:", err) } if w.Code != http.StatusFound { t.Error("Unexpcted status:", w.Code) } loc := w.Header().Get("Location") if loc != authboss.Cfg.AuthLoginSuccessRoute { t.Error("Unexpected redirect:", loc) } } func TestAuth_loginHandlerFunc_GET(t *testing.T) { a, _ := testSetup() ctx, w, r, _ := testRequest("GET") 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, " 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) { authboss.Cfg = authboss.NewConfig() storer := mocks.NewMockStorer() storer.GetErr = "Failed to load user" authboss.Cfg.Storer = storer ctx := authboss.Context{} if err := validateCredentials(&ctx, "", ""); err.Error() != "Failed to load user" { t.Error("Unexpected error:", err) } storer.GetErr = "" storer.Users["john"] = authboss.Attributes{"password": "$2a$10$pgFsuQwdhwOdZp/v52dvHeEi53ZaI7dGmtwK4bAzGGN5A4nT6doqm"} if err := validateCredentials(&ctx, "john", "b"); err == nil { t.Error("Expected error about passwords mismatch") } sessions := mocks.NewMockClientStorer() ctx.SessionStorer = sessions if err := validateCredentials(&ctx, "john", "a"); err != nil { t.Error("Unexpected error:", err) } 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") } } func TestAuth_logoutHandlerFunc_GET(t *testing.T) { a, _ := testSetup() authboss.Cfg.AuthLogoutRoute = "/dashboard" ctx, w, r, sessionStorer := testRequest("GET") sessionStorer.Put(authboss.SessionKey, "asdf") if err := a.logoutHandlerFunc(ctx, w, r); err != nil { t.Error("Unexpected error:", err) } if _, ok := sessionStorer.Get(authboss.SessionKey); ok { t.Errorf("Expected to be logged out") } 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 } } }