127 lines
4.0 KiB
Go
127 lines
4.0 KiB
Go
package auth
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/netip"
|
|
"testing"
|
|
|
|
"git.noroutes.net/NoRoutes/vibetunes/internal/config"
|
|
)
|
|
|
|
func mustPrefix(t *testing.T, s string) netip.Prefix {
|
|
t.Helper()
|
|
p, err := netip.ParsePrefix(s)
|
|
if err != nil {
|
|
t.Fatalf("ParsePrefix(%q): %v", s, err)
|
|
}
|
|
return p
|
|
}
|
|
|
|
func TestExtAuthTrustsConfiguredPeer(t *testing.T) {
|
|
db := openTestDB(t)
|
|
e := &ExtAuth{DB: db, Config: config.ExtAuthConfig{
|
|
Enabled: true,
|
|
UserHeader: "Remote-User",
|
|
TrustedSources: []netip.Prefix{mustPrefix(t, "127.0.0.1/32")},
|
|
}}
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.RemoteAddr = "127.0.0.1:54321"
|
|
req.Header.Set("Remote-User", "alice")
|
|
|
|
if got := e.IdentityFromRequest(req); got != "alice" {
|
|
t.Fatalf("IdentityFromRequest = %q, want alice", got)
|
|
}
|
|
}
|
|
|
|
func TestExtAuthRejectsUntrustedPeer(t *testing.T) {
|
|
db := openTestDB(t)
|
|
e := &ExtAuth{DB: db, Config: config.ExtAuthConfig{
|
|
Enabled: true,
|
|
UserHeader: "Remote-User",
|
|
TrustedSources: []netip.Prefix{mustPrefix(t, "127.0.0.1/32")},
|
|
}}
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.RemoteAddr = "203.0.113.7:54321" // arbitrary internet client, not the trusted proxy
|
|
req.Header.Set("Remote-User", "alice")
|
|
|
|
if got := e.IdentityFromRequest(req); got != "" {
|
|
t.Fatalf("IdentityFromRequest = %q, want empty for untrusted peer", got)
|
|
}
|
|
}
|
|
|
|
// TestExtAuthIgnoresForwardedHeadersFromPeer is the core anti-spoofing
|
|
// guarantee: an arbitrary internet client cannot impersonate a trusted
|
|
// proxy by setting X-Forwarded-For/X-Real-IP/Forwarded, because those
|
|
// headers are never consulted to decide whether the peer is trusted —
|
|
// only the real TCP RemoteAddr is.
|
|
func TestExtAuthIgnoresForwardedHeadersFromPeer(t *testing.T) {
|
|
db := openTestDB(t)
|
|
e := &ExtAuth{DB: db, Config: config.ExtAuthConfig{
|
|
Enabled: true,
|
|
UserHeader: "Remote-User",
|
|
TrustedSources: []netip.Prefix{mustPrefix(t, "127.0.0.1/32")},
|
|
}}
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.RemoteAddr = "203.0.113.7:54321" // real connection is from an untrusted internet host
|
|
req.Header.Set("X-Forwarded-For", "127.0.0.1")
|
|
req.Header.Set("X-Real-IP", "127.0.0.1")
|
|
req.Header.Set("Forwarded", "for=127.0.0.1")
|
|
req.Header.Set("Remote-User", "admin")
|
|
|
|
if got := e.IdentityFromRequest(req); got != "" {
|
|
t.Fatalf("IdentityFromRequest = %q, want empty: forwarded headers must not establish trust", got)
|
|
}
|
|
}
|
|
|
|
func TestExtAuthDisabledNeverTrusts(t *testing.T) {
|
|
db := openTestDB(t)
|
|
e := &ExtAuth{DB: db, Config: config.ExtAuthConfig{
|
|
Enabled: false,
|
|
UserHeader: "Remote-User",
|
|
TrustedSources: []netip.Prefix{mustPrefix(t, "0.0.0.0/0")},
|
|
}}
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.RemoteAddr = "127.0.0.1:1"
|
|
req.Header.Set("Remote-User", "alice")
|
|
|
|
if got := e.IdentityFromRequest(req); got != "" {
|
|
t.Fatalf("IdentityFromRequest = %q, want empty when ExtAuth disabled", got)
|
|
}
|
|
}
|
|
|
|
func TestExtAuthNoTrustedSourcesNeverTrusts(t *testing.T) {
|
|
db := openTestDB(t)
|
|
e := &ExtAuth{DB: db, Config: config.ExtAuthConfig{Enabled: true, UserHeader: "Remote-User"}}
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
req.RemoteAddr = "127.0.0.1:1"
|
|
req.Header.Set("Remote-User", "alice")
|
|
|
|
if got := e.IdentityFromRequest(req); got != "" {
|
|
t.Fatalf("IdentityFromRequest = %q, want empty with no trusted sources configured", got)
|
|
}
|
|
}
|
|
|
|
func TestExtAuthResolveUserAutoCreates(t *testing.T) {
|
|
db := openTestDB(t)
|
|
e := &ExtAuth{DB: db, Config: config.ExtAuthConfig{AutoCreateUsers: true}}
|
|
u, err := e.ResolveUser("newperson")
|
|
if err != nil {
|
|
t.Fatalf("ResolveUser: %v", err)
|
|
}
|
|
if !u.ExternalAuth {
|
|
t.Fatal("auto-created user should have ExternalAuth set")
|
|
}
|
|
if u.PasswordHash.Valid {
|
|
t.Fatal("auto-created external user should have no password hash")
|
|
}
|
|
}
|
|
|
|
func TestExtAuthResolveUserNoAutoCreate(t *testing.T) {
|
|
db := openTestDB(t)
|
|
e := &ExtAuth{DB: db, Config: config.ExtAuthConfig{AutoCreateUsers: false}}
|
|
if _, err := e.ResolveUser("ghost"); err == nil {
|
|
t.Fatal("expected error when auto-create is disabled and user doesn't exist")
|
|
}
|
|
}
|