116 lines
4.1 KiB
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")
|
|
}
|
|
}
|