mox/http/admin_test.go

130 lines
4 KiB
Go
Raw Normal View History

2023-01-30 16:27:06 +03:00
package http
import (
"context"
"crypto/ed25519"
"net"
"net/http/httptest"
2023-01-30 16:27:06 +03:00
"os"
"testing"
"time"
"golang.org/x/crypto/bcrypt"
"github.com/mjl-/mox/config"
"github.com/mjl-/mox/dns"
"github.com/mjl-/mox/mox-"
)
func TestAdminAuth(t *testing.T) {
test := func(passwordfile, authHdr string, expect bool) {
t.Helper()
w := httptest.NewRecorder()
r := httptest.NewRequest("GET", "/ignored", nil)
if authHdr != "" {
r.Header.Add("Authorization", authHdr)
}
ok := checkAdminAuth(context.Background(), passwordfile, w, r)
2023-01-30 16:27:06 +03:00
if ok != expect {
t.Fatalf("got %v, expected %v", ok, expect)
}
}
const authOK = "Basic YWRtaW46bW94dGVzdDEyMw==" // admin:moxtest123
const authBad = "Basic YWRtaW46YmFkcGFzc3dvcmQ=" // admin:badpassword
const path = "../testdata/http-passwordfile"
os.Remove(path)
defer os.Remove(path)
test(path, authOK, false) // Password file does not exist.
adminpwhash, err := bcrypt.GenerateFromPassword([]byte("moxtest123"), bcrypt.DefaultCost)
if err != nil {
t.Fatalf("generate bcrypt hash: %v", err)
}
if err := os.WriteFile(path, adminpwhash, 0660); err != nil {
t.Fatalf("write password file: %v", err)
}
// We loop to also exercise the auth cache.
for i := 0; i < 2; i++ {
test(path, "", false) // Empty/missing header.
test(path, "Malformed ", false) // Not "Basic"
test(path, "Basic malformed ", false) // Bad base64.
test(path, "Basic dGVzdA== ", false) // base64 is ok, but wrong tokens inside.
test(path, authBad, false) // Wrong password.
test(path, authOK, true)
}
}
func TestCheckDomain(t *testing.T) {
// NOTE: we aren't currently looking at the results, having the code paths executed is better than nothing.
resolver := dns.MockResolver{
MX: map[string][]*net.MX{
"mox.example.": {{Host: "mail.mox.example.", Pref: 10}},
},
A: map[string][]string{
"mail.mox.example.": {"127.0.0.2"},
},
AAAA: map[string][]string{
"mail.mox.example.": {"127.0.0.2"},
},
TXT: map[string][]string{
"mox.example.": {"v=spf1 mx -all"},
"test._domainkey.mox.example.": {"v=DKIM1;h=sha256;k=ed25519;p=ln5zd/JEX4Jy60WAhUOv33IYm2YZMyTQAdr9stML504="},
"_dmarc.mox.example.": {"v=DMARC1; p=reject; rua=mailto:mjl@mox.example"},
"_smtp._tls.mox.example": {"v=TLSRPTv1; rua=mailto:tlsrpt@mox.example;"},
"_mta-sts.mox.example": {"v=STSv1; id=20160831085700Z"},
},
CNAME: map[string]string{},
}
listener := config.Listener{
IPs: []string{"127.0.0.2"},
Hostname: "mox.example",
HostnameDomain: dns.Domain{ASCII: "mox.example"},
}
listener.SMTP.Enabled = true
listener.AutoconfigHTTPS.Enabled = true
listener.MTASTSHTTPS.Enabled = true
mox.Conf.Static.Listeners = map[string]config.Listener{
"public": listener,
}
domain := config.Domain{
DKIM: config.DKIM{
Selectors: map[string]config.Selector{
"test": {
HashEffective: "sha256",
HeadersEffective: []string{"From", "Date", "Subject"},
Key: ed25519.NewKeyFromSeed(make([]byte, 32)), // warning: fake zero key, do not copy this code.
Domain: dns.Domain{ASCII: "test"},
},
"missing": {
HashEffective: "sha256",
HeadersEffective: []string{"From", "Date", "Subject"},
Key: ed25519.NewKeyFromSeed(make([]byte, 32)), // warning: fake zero key, do not copy this code.
Domain: dns.Domain{ASCII: "missing"},
},
},
Sign: []string{"test", "test2"},
},
}
mox.Conf.Dynamic.Domains = map[string]config.Domain{
"mox.example": domain,
}
// Make a dialer that fails immediately before actually connecting.
done := make(chan struct{})
close(done)
dialer := &net.Dialer{Deadline: time.Now().Add(-time.Second), Cancel: done}
checkDomain(context.Background(), resolver, dialer, "mox.example")
// todo: check returned data
Admin{}.Domains(context.Background()) // todo: check results
dnsblsStatus(context.Background(), resolver) // todo: check results
}