notea-de-me/server/authenticate_test.go

270 lines
6.1 KiB
Go

package main
import (
"fmt"
"net/http"
"net/http/httptest"
"path"
"testing"
"time"
"github.com/google/uuid"
)
func TestEncodeDecodeCookie(t *testing.T) {
newTestServer(t)
for i := 0; i < 5; i++ {
value := uuid.New().String()
encoded := encodeCookie(value)
for j := 0; j < 5; j++ {
decoded, ok := decodeCookie(encoded)
if !ok || decoded != value {
t.Errorf("value=%s, encoded=%s, decoded=%s", value, encoded, decoded)
}
}
}
}
func TestEncodeDecodeUserCookie(t *testing.T) {
newTestServer(t)
user := User{
User: "abc",
Groups: []string{"def", "ghi"},
}
encoded := encodeUserCookie(user)
decoded, ok := decodeUserCookie(encoded)
if !ok {
t.Fatal(ok)
}
if fmt.Sprint(user) != fmt.Sprint(decoded) {
t.Fatal(user, decoded)
}
}
func TestGetCookie(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/", nil)
r.AddCookie(&http.Cookie{
Name: "abc",
Value: "def",
Expires: time.Now().Add(time.Hour),
})
got, _ := getCookie("abc", r)
if got != "def" {
t.Fatal(r.Cookies(), got)
}
}
func TestGetSetLoginCookie(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/", nil)
user := User{User: "a", Groups: []string{"g"}}
setLoginCookie(w, r, user)
if w.Header().Get("Set-Cookie") == "" {
t.Error(w.Header())
}
if r.Cookies()[0].Name != "login" {
t.Error(r.Cookies())
}
got, ok := loginCookie(r)
if !ok {
t.Error(ok)
}
if fmt.Sprint(user) != fmt.Sprint(got) {
t.Error(user, got)
}
}
func TestNeedsLogin(t *testing.T) {
w := httptest.NewRecorder()
user := User{User: "user", Groups: []string{"group0", "group1"}}
t.Run("no login provided", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/", nil)
if ok, err := needsLogin(r); err != nil {
t.Fatal(err)
} else if !ok {
t.Fatal(ok)
}
})
t.Run("no namespace provided", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/", nil)
setLoginCookie(w, r, user)
if ok, err := needsLogin(r); err != nil {
t.Fatal(err)
} else if !ok {
t.Fatal(ok)
}
})
t.Run("cookie tampered", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/", nil)
setLoginCookie(w, r, user)
setNamespaceCookie(w, r, user.Groups[0])
cookieSecret += "modified"
if ok, err := needsLogin(r); err != nil {
t.Fatal(err)
} else if !ok {
t.Fatal(ok)
}
})
t.Run("bad namespace", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/", nil)
setLoginCookie(w, r, user)
setNamespaceCookie(w, r, "teehee")
if ok, err := needsLogin(r); err != nil {
t.Fatal(err)
} else if !ok {
t.Fatal(ok)
}
})
t.Run("ok", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/", nil)
setLoginCookie(w, r, user)
setNamespaceCookie(w, r, user.Groups[0])
if ok, err := needsLogin(r); err != nil {
t.Fatal(err)
} else if ok {
t.Fatal(ok)
}
})
}
func TestServerParseLogin(t *testing.T) {
server := newTestServer(t)
t.Run("no basic auth", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/", nil)
if done, err := server.parseLogin(w, r); done || err != nil {
t.Fatal(done, err)
}
if w.Code == http.StatusUnauthorized {
t.Error(w.Code)
}
})
t.Run("bad basic auth", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/", nil)
r.SetBasicAuth("junk", "junk")
if done, err := server.parseLogin(w, r); !done || err != nil {
t.Fatal(done, err)
}
if w.Code != http.StatusUnauthorized {
t.Error(w.Code)
}
})
t.Run("ok", func(t *testing.T) {
w := httptest.NewRecorder()
r := httptest.NewRequest(http.MethodGet, "/", nil)
r.SetBasicAuth("user", "passw")
if done, err := server.parseLogin(w, r); done || err != nil {
t.Fatal(done, err)
}
if w.Code == http.StatusUnauthorized {
t.Error(w.Code)
}
if len(w.Header()["Set-Cookie"]) != 2 {
t.Error(w.Header())
}
if len(r.Cookies()) != 2 {
t.Error(r.Cookies())
}
if v, ok := namespaceCookie(r); !ok || v != "group" {
t.Error(r.Cookies())
}
if user, ok := loginCookie(r); !ok || user.User != "user" || user.Groups[0] != "group" || user.Groups[1] != "othergroup" {
t.Error(user)
}
})
}
func TestServerAuthenticate(t *testing.T) {
server := newTestServer(t)
t.Run("ok: already logged in", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/", nil)
setLoginCookie(httptest.NewRecorder(), r, User{User: "user", Groups: []string{"group", "othergroup"}})
setNamespaceCookie(httptest.NewRecorder(), r, "othergroup")
s2, done, err := server.authenticate(nil, r)
if err != nil {
t.Error(err)
}
if done {
t.Error(done)
}
if server == s2 {
t.Error(done)
}
if server.loggedIn != nil {
t.Error(server.loggedIn)
}
if s2.loggedIn == nil {
t.Error(s2.loggedIn)
}
if s2.loggedIn.user != "user" {
t.Error(s2.loggedIn)
}
if s2.loggedIn.group != "othergroup" {
t.Error(s2.loggedIn)
}
if fmt.Sprint(s2.loggedIn.groups) != fmt.Sprint([]string{"group", "othergroup"}) {
t.Error(s2.loggedIn)
}
})
t.Run("ok: basic auth", func(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/", nil)
w := httptest.NewRecorder()
r.SetBasicAuth("user", "passw")
s2, done, err := server.authenticate(w, r)
if err != nil {
t.Error(err)
}
if done {
t.Error(done)
}
if server == s2 {
t.Error(done)
}
if server.loggedIn != nil {
t.Error(server.loggedIn)
}
if s2.loggedIn == nil {
t.Error(s2.loggedIn)
}
if s2.loggedIn.user != "user" {
t.Error(s2.loggedIn)
}
if s2.loggedIn.group != "group" {
t.Error(s2.loggedIn)
}
if fmt.Sprint(s2.loggedIn.groups) != fmt.Sprint([]string{"group", "othergroup"}) {
t.Error(s2.loggedIn)
}
if w.Code != http.StatusOK {
t.Error(w.Code)
}
if len(w.Header()["Set-Cookie"]) != 2 {
t.Error(w.Header())
}
})
}
func newTestServer(t *testing.T) *Server {
cookieSecret = uuid.New().String()
p := path.Join(t.TempDir(), "auth.yaml")
ensureAndWrite(p, []byte(`{"users":{"user":{"password":"passw", "groups":["group", "othergroup"]}}}`))
return &Server{
auth: NewFileAuth(p),
}
}