Foundation: config, schema/migrations, auth, stable IDs, metadata probing
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user