PrintToVeraeTimeStamp/internal/auth/portal_test.go

116 lines
4.1 KiB
Go

package auth
import (
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
)
func fakePortal(t *testing.T, installAPI bool) *httptest.Server {
mux := http.NewServeMux()
js := func(w http.ResponseWriter, code int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
_ = json.NewEncoder(w).Encode(v)
}
body := func(r *http.Request) map[string]any {
m := map[string]any{}
_ = json.NewDecoder(r.Body).Decode(&m)
return m
}
if installAPI {
mux.HandleFunc("/portal/api/install/capabilities", func(w http.ResponseWriter, r *http.Request) {
js(w, 200, map[string]any{"installApi": true, "emailVerification": true, "passwordPolicy": map[string]any{"minLength": 12, "requireLetter": true, "requireDigit": true}})
})
mux.HandleFunc("/portal/api/install/register", func(w http.ResponseWriter, r *http.Request) { js(w, 202, map[string]any{"pendingId": "p1"}) })
mux.HandleFunc("/portal/api/install/verify", func(w http.ResponseWriter, r *http.Request) {
if body(r)["code"] != "123456" {
js(w, 400, map[string]any{"error": "invalid_code"})
return
}
js(w, 201, map[string]any{"token": "xVerae-verified-abcdefgh"})
})
mux.HandleFunc("/portal/api/install/login", func(w http.ResponseWriter, r *http.Request) {
b := body(r)
if b["totpCode"] == nil {
js(w, 401, map[string]any{"error": "totp_required"})
return
}
js(w, 200, map[string]any{"token": "xVerae-login-abcdefgh"})
})
}
mux.HandleFunc("/portal/api/login", func(w http.ResponseWriter, r *http.Request) { js(w, 200, map[string]any{"token": "sess"}) })
mux.HandleFunc("/portal/api/signup", func(w http.ResponseWriter, r *http.Request) { js(w, 200, map[string]any{"token": "sess"}) })
mux.HandleFunc("/portal/api/tokens", func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") != "Bearer sess" {
js(w, 401, map[string]any{"error": "no"})
return
}
js(w, 200, map[string]any{"token": "xVerae-minted-abcdefgh"})
})
mux.HandleFunc("/portal/api/logout", func(w http.ResponseWriter, r *http.Request) { js(w, 200, map[string]any{}) })
s := httptest.NewServer(mux)
t.Cleanup(s.Close)
t.Setenv("VERAE_PORTAL_BASE", s.URL)
t.Setenv("VERAE_ALLOW_INSECURE_PORTAL", "1")
return s
}
func TestPortalFallback(t *testing.T) {
fakePortal(t, false)
caps := GetCapabilities()
if caps.InstallAPI || caps.Policy.MinLength != DefaultPolicy.MinLength {
t.Fatalf("caps %+v", caps)
}
k, err := InstallLogin(caps, "a@b.co", "pw", "", "n")
if err != nil || k != "xVerae-minted-abcdefgh" {
t.Fatal(k, err)
}
rr, err := InstallRegister(caps, "N", "a@b.co", "pw", "n")
if err != nil || rr.Key == "" || rr.PendingID != "" {
t.Fatal(rr, err)
}
}
func TestPortalInstallAPI(t *testing.T) {
fakePortal(t, true)
caps := GetCapabilities()
if !caps.InstallAPI || caps.Policy.MinLength != 12 {
t.Fatalf("caps %+v", caps)
}
if _, err := InstallLogin(caps, "a@b.co", "pw", "", "n"); !errors.Is(err, ErrTOTPRequired) {
t.Fatal("want totp_required", err)
}
if k, err := InstallLogin(caps, "a@b.co", "pw", "123456", "n"); err != nil || k != "xVerae-login-abcdefgh" {
t.Fatal(k, err)
}
rr, err := InstallRegister(caps, "N", "a@b.co", "pw", "n")
if err != nil || rr.PendingID != "p1" {
t.Fatal(rr, err)
}
if _, err := InstallVerify("p1", "000000", "n"); err == nil {
t.Fatal("bad code accepted")
}
if k, err := InstallVerify("p1", "123456", "n"); err != nil || k != "xVerae-verified-abcdefgh" {
t.Fatal(k, err)
}
}
func TestPolicyAndHTTPS(t *testing.T) {
p := DefaultPolicy
for pw, ok := range map[string]bool{"short1": false, "longenoughnodigit": false, "longenough123": true, "george12345x": false} {
if (p.Check(pw, "george@x.com") == nil) != ok {
t.Errorf("policy %q want ok=%v", pw, ok)
}
}
t.Setenv("VERAE_PORTAL_BASE", "http://example.com")
t.Setenv("VERAE_ALLOW_INSECURE_PORTAL", "")
if _, _, err := portalPost("/login", "", map[string]any{}); err == nil {
t.Fatal("non-https portal allowed")
}
if MaskKey("xVerae-abcdefghijklmnop") != "xVerae-...mnop" || MaskEmail("george@x.com") != "g***@x.com" {
t.Fatal("masking")
}
}