fix: accept bare Pi-hole IPv4 addresses
This commit is contained in:
1 parent
42fa6efc84
commit
2082b68794
5 files changed
+15
-7
No files matched your search
@@ -71,7 +71,11 @@ func New(rawURL string, insecureTLS bool) (*Client, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func normalizeBaseURL(raw string) (string, error) {
|
func normalizeBaseURL(raw string) (string, error) {
|
||||||
parsed, err := url.Parse(strings.TrimSpace(raw))
|
raw = strings.TrimSpace(raw)
|
||||||
|
if address := net.ParseIP(raw); address != nil && address.To4() != nil {
|
||||||
|
raw = "http://" + raw
|
||||||
|
}
|
||||||
|
parsed, err := url.Parse(raw)
|
||||||
if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") {
|
if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") {
|
||||||
return "", errors.New("Pi-hole-Adresse muss eine vollständige HTTP- oder HTTPS-URL sein")
|
return "", errors.New("Pi-hole-Adresse muss eine vollständige HTTP- oder HTTPS-URL sein")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ func TestNewValidatesAndNormalizesPiHoleURL(t *testing.T) {
|
|||||||
if err != nil || client.BaseURL() != "http://pihole.local/custom/api" {
|
if err != nil || client.BaseURL() != "http://pihole.local/custom/api" {
|
||||||
t.Fatalf("custom path not normalized: %q err=%v", client.BaseURL(), err)
|
t.Fatalf("custom path not normalized: %q err=%v", client.BaseURL(), err)
|
||||||
}
|
}
|
||||||
|
client, err = New("10.0.20.100", false)
|
||||||
|
if err != nil || client.BaseURL() != "http://10.0.20.100/api" {
|
||||||
|
t.Fatalf("bare IPv4 address not normalized: %q err=%v", client.BaseURL(), err)
|
||||||
|
}
|
||||||
for _, invalid := range []string{"pi.hole", "ftp://pi.hole", "https://user:pass@pi.hole", "https://pi.hole?sid=secret"} {
|
for _, invalid := range []string{"pi.hole", "ftp://pi.hole", "https://user:pass@pi.hole", "https://pi.hole?sid=secret"} {
|
||||||
if _, err := New(invalid, false); err == nil {
|
if _, err := New(invalid, false); err == nil {
|
||||||
t.Errorf("invalid URL accepted: %s", invalid)
|
t.Errorf("invalid URL accepted: %s", invalid)
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ func TestPiHolePageRendersConnectionAndLocalDNSControls(t *testing.T) {
|
|||||||
t.Fatalf("render Pi-hole page: %v", err)
|
t.Fatalf("render Pi-hole page: %v", err)
|
||||||
}
|
}
|
||||||
body := rendered.String()
|
body := rendered.String()
|
||||||
for _, expected := range []string{`href="/pihole"`, `aria-current="page"`, `action="/pihole/7/records"`, `action="/pihole/7/records/update"`, `action="/pihole/7/records/archive"`, `action="/pihole/7/records/restore"`, `action="/pihole/7/records/delete"`, `action="/pihole/7/remove"`, `action="/pihole/7/delete"`, `action="/pihole/7/save"`, `action="/pihole/7/password"`, `name="active"`, `name="link_id" value="11"`, `name="insecure_tls"`, `data-dirty-form`, `data-dirty-submit disabled aria-disabled="true"`, `data-scroll-page-end`, `Anwendungspasswort anzeigen`, `Passwort entschlüsseln`, `Nur aus der WebUI entfernen`, `Anwendungspasswort`, `nas.lan`, `old.lan`, `192.168.1.20`} {
|
for _, expected := range []string{`href="/pihole"`, `aria-current="page"`, `action="/pihole/7/records"`, `action="/pihole/7/records/update"`, `action="/pihole/7/records/archive"`, `action="/pihole/7/records/restore"`, `action="/pihole/7/records/delete"`, `action="/pihole/7/remove"`, `action="/pihole/7/delete"`, `action="/pihole/7/save"`, `action="/pihole/7/password"`, `name="active"`, `name="link_id" value="11"`, `name="insecure_tls"`, `name="base_url" type="text"`, `data-dirty-form`, `data-dirty-submit disabled aria-disabled="true"`, `data-scroll-page-end`, `Anwendungspasswort anzeigen`, `Passwort entschlüsseln`, `Nur aus der WebUI entfernen`, `Anwendungspasswort`, `nas.lan`, `old.lan`, `192.168.1.20`} {
|
||||||
if !strings.Contains(body, expected) {
|
if !strings.Contains(body, expected) {
|
||||||
t.Errorf("Pi-hole page is missing %q", expected)
|
t.Errorf("Pi-hole page is missing %q", expected)
|
||||||
}
|
}
|
||||||
@@ -359,7 +359,7 @@ func TestPiHoleDisconnectedPageCentersConnectFormWithoutEmptyState(t *testing.T)
|
|||||||
t.Fatalf("render disconnected Pi-hole page: %v", err)
|
t.Fatalf("render disconnected Pi-hole page: %v", err)
|
||||||
}
|
}
|
||||||
body := rendered.String()
|
body := rendered.String()
|
||||||
for _, expected := range []string{`class="pihole-connect-stage"`, `class="form-panel pihole-connect-panel"`, `action="/pihole/connect"`} {
|
for _, expected := range []string{`class="pihole-connect-stage"`, `class="form-panel pihole-connect-panel"`, `action="/pihole/connect"`, `name="base_url" type="text"`, `10.0.20.100 oder https://pi.hole`} {
|
||||||
if !strings.Contains(body, expected) {
|
if !strings.Contains(body, expected) {
|
||||||
t.Errorf("disconnected Pi-hole page is missing %q", expected)
|
t.Errorf("disconnected Pi-hole page is missing %q", expected)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<meta name="color-scheme" content="light">
|
<meta name="color-scheme" content="light">
|
||||||
<title>{{.Title}} · DNS Control</title>
|
<title>{{.Title}} · DNS Control</title>
|
||||||
<link rel="stylesheet" href="/static/styles.css?v=features-14">
|
<link rel="stylesheet" href="/static/styles.css?v=features-15">
|
||||||
<script src="/static/app.js?v=features-14" defer></script>
|
<script src="/static/app.js?v=features-15" defer></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="page-{{.Page}}">
|
<body class="page-{{.Page}}">
|
||||||
<a class="skip-link" href="#main">Zum Inhalt springen</a>
|
<a class="skip-link" href="#main">Zum Inhalt springen</a>
|
||||||
|
|||||||
@@ -69,7 +69,7 @@
|
|||||||
</article>
|
</article>
|
||||||
<article class="panel" id="pihole-connection"><div class="panel-heading"><div><span class="section-label pihole-kicker">API-Zugang</span><h2>Verbindung verwalten</h2></div><span class="status-pill {{if $view.FetchError}}status-bad{{else}}pihole-status-pill{{end}}">{{if $view.FetchError}}Nicht erreichbar{{else}}Verbunden{{end}}</span></div>
|
<article class="panel" id="pihole-connection"><div class="panel-heading"><div><span class="section-label pihole-kicker">API-Zugang</span><h2>Verbindung verwalten</h2></div><span class="status-pill {{if $view.FetchError}}status-bad{{else}}pihole-status-pill{{end}}">{{if $view.FetchError}}Nicht erreichbar{{else}}Verbunden{{end}}</span></div>
|
||||||
<p class="panel-description">Das Anwendungspasswort bleibt verschlüsselt und standardmäßig verborgen. Eine Anzeige erfordert erneut dein Admin-Passwort.</p>
|
<p class="panel-description">Das Anwendungspasswort bleibt verschlüsselt und standardmäßig verborgen. Eine Anzeige erfordert erneut dein Admin-Passwort.</p>
|
||||||
<form action="/pihole/{{$integration.ID}}/save" method="post" class="stacked-form" data-dirty-form><input type="hidden" name="csrf" value="{{.CSRF}}"><label>Pi-hole-Adresse <input name="base_url" type="url" value="{{$integration.BaseURL}}" required><small>Basis-URL oder vollständiger <code>/api</code>-Pfad.</small></label><label>Neues Anwendungspasswort <input name="password" type="password" autocomplete="new-password"><small>Leer lassen, um das gespeicherte Passwort beizubehalten.</small></label><label class="switch pihole-switch"><input type="checkbox" name="insecure_tls" {{if $integration.InsecureTLS}}checked{{end}}><span></span> TLS-Zertifikat nicht prüfen</label><button class="button button-pihole" type="submit" data-dirty-submit disabled aria-disabled="true">Verbindung prüfen und speichern</button></form>
|
<form action="/pihole/{{$integration.ID}}/save" method="post" class="stacked-form" data-dirty-form><input type="hidden" name="csrf" value="{{.CSRF}}"><label>Pi-hole-Adresse <input name="base_url" type="text" inputmode="url" autocapitalize="none" spellcheck="false" value="{{$integration.BaseURL}}" required><small>IPv4-Adresse oder Basis-URL; <code>/api</code> wird automatisch ergänzt.</small></label><label>Neues Anwendungspasswort <input name="password" type="password" autocomplete="new-password"><small>Leer lassen, um das gespeicherte Passwort beizubehalten.</small></label><label class="switch pihole-switch"><input type="checkbox" name="insecure_tls" {{if $integration.InsecureTLS}}checked{{end}}><span></span> TLS-Zertifikat nicht prüfen</label><button class="button button-pihole" type="submit" data-dirty-submit disabled aria-disabled="true">Verbindung prüfen und speichern</button></form>
|
||||||
<div class="panel-divider"></div>
|
<div class="panel-divider"></div>
|
||||||
<details><summary class="button button-quiet">Anwendungspasswort anzeigen</summary><form action="/pihole/{{$integration.ID}}/password" method="post" class="stacked-form inset-form"><input type="hidden" name="csrf" value="{{$.CSRF}}"><label>Admin-Passwort <input type="password" name="password" autocomplete="current-password" required></label><button class="button button-danger-soft" type="submit">Passwort entschlüsseln</button></form></details>
|
<details><summary class="button button-quiet">Anwendungspasswort anzeigen</summary><form action="/pihole/{{$integration.ID}}/password" method="post" class="stacked-form inset-form"><input type="hidden" name="csrf" value="{{$.CSRF}}"><label>Admin-Passwort <input type="password" name="password" autocomplete="current-password" required></label><button class="button button-danger-soft" type="submit">Passwort entschlüsseln</button></form></details>
|
||||||
<div class="panel-divider"></div>
|
<div class="panel-divider"></div>
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
<div class="form-panel pihole-connect-panel" id="pihole-connect">
|
<div class="form-panel pihole-connect-panel" id="pihole-connect">
|
||||||
<div class="panel-heading"><div><span class="section-label pihole-kicker">API-Verbindung</span><h2 id="pihole-connect-title">Pi-hole verbinden</h2></div><span class="pihole-api-badge">API v6</span></div>
|
<div class="panel-heading"><div><span class="section-label pihole-kicker">API-Verbindung</span><h2 id="pihole-connect-title">Pi-hole verbinden</h2></div><span class="pihole-api-badge">API v6</span></div>
|
||||||
<p class="panel-description">Verwende ein eigenes Pi-hole-Anwendungspasswort. Es wird mit dem Master-Key dieser Anwendung verschlüsselt.</p>
|
<p class="panel-description">Verwende ein eigenes Pi-hole-Anwendungspasswort. Es wird mit dem Master-Key dieser Anwendung verschlüsselt.</p>
|
||||||
<form action="/pihole/connect" method="post" class="stacked-form"><input type="hidden" name="csrf" value="{{.CSRF}}"><label>Pi-hole-Adresse <input name="base_url" type="url" placeholder="https://pi.hole" required><small><code>/api</code> wird automatisch ergänzt.</small></label><label>Anwendungspasswort <input name="password" type="password" autocomplete="new-password" required><small>Keinen Session-Token eintragen.</small></label><label class="switch pihole-switch"><input type="checkbox" name="insecure_tls"><span></span> TLS-Zertifikat nicht prüfen</label><div class="warning-box">Nur für ein selbstsigniertes Zertifikat in einem vertrauenswürdigen internen Netz verwenden. Bei HTTP wird das Passwort auf dem Netzwerkweg unverschlüsselt übertragen.</div><button class="button button-pihole" type="submit">Verbindung testen und speichern</button></form>
|
<form action="/pihole/connect" method="post" class="stacked-form"><input type="hidden" name="csrf" value="{{.CSRF}}"><label>Pi-hole-Adresse <input name="base_url" type="text" inputmode="url" autocapitalize="none" spellcheck="false" placeholder="10.0.20.100 oder https://pi.hole" required><small>IPv4-Adressen werden automatisch über HTTP verbunden; <code>/api</code> wird ergänzt.</small></label><label>Anwendungspasswort <input name="password" type="password" autocomplete="new-password" required><small>Keinen Session-Token eintragen.</small></label><label class="switch pihole-switch"><input type="checkbox" name="insecure_tls"><span></span> TLS-Zertifikat nicht prüfen</label><div class="warning-box">Nur für ein selbstsigniertes Zertifikat in einem vertrauenswürdigen internen Netz verwenden. Bei HTTP wird das Passwort auf dem Netzwerkweg unverschlüsselt übertragen.</div><button class="button button-pihole" type="submit">Verbindung testen und speichern</button></form>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
Reference in new issue
Block a user