Files
mailgoserver/internal/webui/settings_mfa_test.go
T

99 lines
3.9 KiB
Go

package webui
import (
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
)
// TestSettingsRejectsEnablingAdminMFAWithoutOwnMFA guards against a real self-lockout
// bug: enabling enforce_admin_mfa immediately blocks every admin route (including
// /settings itself) for any admin without their own MFA configured. Without this
// precondition, an admin could flip the toggle on and lock themselves out with no way
// back in short of editing the database directly.
func TestSettingsRejectsEnablingAdminMFAWithoutOwnMFA(t *testing.T) {
app := newTestApp(t)
mux := app.Mux()
cookie := loginSession(t, app) // loginSession's admin has no MFA configured
form := baseSettingsForm()
form.Set("Auth.enforce_admin_mfa", "true")
req := httptest.NewRequest(http.MethodPost, Prefix+"/settings_update", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.AddCookie(cookie)
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
if rec.Code != http.StatusFound {
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
}
if got := app.Cfg.Section("Auth").Key("enforce_admin_mfa").Value(); got != "false" {
t.Fatalf("enforce_admin_mfa = %q, want unchanged (still false) since the admin has no MFA", got)
}
// The same admin must still be able to reach every other page — the whole point
// of rejecting the save is that nothing actually changed.
req = httptest.NewRequest(http.MethodGet, Prefix+"/domains", nil)
req.AddCookie(cookie)
rec = httptest.NewRecorder()
mux.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected /domains still reachable, got %d", rec.Code)
}
}
// TestSettingsAllowsEnablingAdminMFAWithOwnMFA confirms the precondition isn't just a
// blanket rejection — an admin who already has MFA set up can turn enforcement on.
func TestSettingsAllowsEnablingAdminMFAWithOwnMFA(t *testing.T) {
app := newTestApp(t)
mux := app.Mux()
cookie := loginSession(t, app)
sess, err := app.DB.GetSession(cookie.Value)
if err != nil || sess == nil {
t.Fatal(err)
}
if err := app.DB.SetAdminTOTPSecret(sess.UserID, "JBSWY3DPEHPK3PXP", true); err != nil {
t.Fatal(err)
}
form := baseSettingsForm()
form.Set("Auth.enforce_admin_mfa", "true")
req := httptest.NewRequest(http.MethodPost, Prefix+"/settings_update", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.AddCookie(cookie)
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, req)
if rec.Code != http.StatusFound {
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
}
if got := app.Cfg.Section("Auth").Key("enforce_admin_mfa").Value(); got != "true" {
t.Fatalf("enforce_admin_mfa = %q, want true", got)
}
}
// baseSettingsForm returns a full, valid settings_update submission (every field
// unchanged from newTestApp's fixture) — settingsUpdate iterates every existing ini
// key and only touches ones present in the form, but real form submissions always
// include every field on the page, so tests should too.
func baseSettingsForm() url.Values {
return url.Values{
"Server.smtp_port": {"4025"}, "Server.smtp_tls_port": {"40465"},
"Server.web_http_port": {"5000"}, "Server.web_https_port": {"5001"},
"Server.bind_ip": {"0.0.0.0"}, "Server.time_zone": {"UTC"},
"Server.hostname": {"mail.example.com"}, "Server.helo_hostname": {"mail.example.com"},
"Server.server_banner": {""},
"Database.database_url": {"sqlite:///server_data/smtp_server.db"},
"Logging.log_level": {"INFO"}, "Logging.hide_info_aiosmtpd": {"true"},
"Relay.relay_timeout": {"30"},
"TLS.tls_cert_file": {"ssl_certs/server.crt"}, "TLS.tls_key_file": {"ssl_certs/server.key"},
"DKIM.dkim_key_size": {"2048"}, "DKIM.spf_server_ip": {"192.168.1.1"},
"Attachments.attachments_path": {"server_data/attachments"},
"IMAP.imap_port": {"1143"}, "IMAP.imap_tls_port": {"1993"},
"Auth.enforce_admin_mfa": {"false"}, "Auth.enforce_mailbox_mfa": {"false"},
}
}