Files
Halcyon/api/admin_internal_test.go
2026-10-01 22:16:48 +02:00

84 lines
2.7 KiB
Go

package api
import (
"strings"
"testing"
"time"
)
func TestAdminSessionTokens(t *testing.T) {
a := newAdminAuth("correct horse battery")
now := time.Unix(1_800_000_000, 0)
tok := a.token(now)
if !a.valid(tok, now) || !a.valid(tok, now.Add(adminSessionTTL-time.Second)) {
t.Fatal("a fresh token should be valid for its whole lifetime")
}
if a.valid(tok, now.Add(adminSessionTTL)) || a.valid(tok, now.Add(24*time.Hour)) {
t.Fatal("an expired token was accepted")
}
exp, mac, _ := strings.Cut(tok, ".")
for name, bad := range map[string]string{
"empty": "",
"no separator": "12345",
"wrong signature": exp + "." + strings.Repeat("0", len(mac)),
"extended expiry": "9999999999." + mac, // keep the signature, lengthen the life
"not a number": "abc." + a.sign("abc"),
"signature missing": exp + ".",
} {
if a.valid(bad, now) {
t.Errorf("%s: forged token accepted", name)
}
}
// Another password derives another key: tokens do not carry over.
if newAdminAuth("a different password").valid(tok, now) {
t.Fatal("a token signed under one password was accepted under another")
}
}
func TestAdminPasswordCheck(t *testing.T) {
a := newAdminAuth("correct horse battery")
if !a.passwordOK("correct horse battery") {
t.Fatal("the right password was refused")
}
for _, pw := range []string{"", "correct horse batter", "correct horse battery ", "CORRECT HORSE BATTERY"} {
if a.passwordOK(pw) {
t.Errorf("%q accepted", pw)
}
}
}
func TestAdminLockoutWindow(t *testing.T) {
a := newAdminAuth("pw")
now := time.Unix(1_800_000_000, 0)
for i := 0; i < adminMaxFails-1; i++ {
a.recordFailure("1.2.3.4", now)
if _, locked := a.lockedOut("1.2.3.4", now); locked {
t.Fatalf("locked out after only %d failures", i+1)
}
}
a.recordFailure("1.2.3.4", now)
wait, locked := a.lockedOut("1.2.3.4", now)
if !locked || wait != adminLockout {
t.Fatalf("after %d failures: locked=%v wait=%v", adminMaxFails, locked, wait)
}
if _, locked := a.lockedOut("5.6.7.8", now); locked {
t.Fatal("one address's failures locked out another")
}
if _, locked := a.lockedOut("1.2.3.4", now.Add(adminLockout+time.Second)); locked {
t.Fatal("the lockout never ends")
}
// Failures that are spread out over more than the window do not add up.
b := newAdminAuth("pw")
for i := 0; i < adminMaxFails+3; i++ {
b.recordFailure("9.9.9.9", now.Add(time.Duration(i)*adminLockout))
}
if _, locked := b.lockedOut("9.9.9.9", now.Add(time.Duration(adminMaxFails+3)*adminLockout)); locked {
t.Fatal("slow guessing should not trip the lockout")
}
a.clear("1.2.3.4")
if _, locked := a.lockedOut("1.2.3.4", now); locked {
t.Fatal("a successful login should clear the failures")
}
}