v0.332.0: one sign-in for the file manager — a dashboard pass, a host-only files cookie, a per-box secret header (R-1100, operator D16)
gates / gates (push) Successful in 47s

This commit is contained in:
2026-10-19 04:56:56 +02:00
parent ba4a6f060b
commit 9bdd8f8fe8
21 changed files with 1078 additions and 54 deletions
+60 -19
View File
@@ -2,10 +2,12 @@ package web
import (
"bytes"
"html"
"io"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"regexp"
@@ -181,6 +183,34 @@ func lfGet(t *testing.T, s *Server, path string) (int, string, string) {
var lfAnswerRe = regexp.MustCompile(`data-login-answer="([a-z_]+)"`)
var lfFilesRe = regexp.MustCompile(`href="([^"]+)"[^>]*data-app-files="1"`)
// lfOpenTo is the FileBrowser path a rendered „Fájlok" href opens since R-1100: the href is the dashboard's
// /__files/open, the folder rides `to`. "" = not such a link.
func lfOpenTo(href string) string {
u, err := url.Parse(html.UnescapeString(href))
if err != nil || u.Host != "felhom.example.hu" || u.Path != "/__files/open" {
return ""
}
return u.Query().Get("to")
}
// lfOpenHrefs are the FileBrowser paths of every dashboard pass link on a page.
func lfOpenHrefs(page string) []string {
var out []string
for _, m := range regexp.MustCompile(`href="(https://felhom\.example\.hu/__files/open\?[^"]+)"`).FindAllStringSubmatch(page, -1) {
out = append(out, lfOpenTo(m[1]))
}
return out
}
func lfHasOpen(page, path string) bool {
for _, p := range lfOpenHrefs(page) {
if p == path {
return true
}
}
return false
}
// ── Part 1: FileBrowser is part of the box ─────────────────────────────────────────────────────────────────────────
// RED-PROOF (2026-10-16, REPORT): with appPageHandler reading `installed := found.Deployed` again (the v0.315.0
@@ -277,32 +307,38 @@ func TestAppPage_EveryInstalledAppHasOneLoginAnswer(t *testing.T) {
// calibre-web — the button opened the import drop-zone, the first declared folder.
func TestAppPage_FilesButtonTarget(t *testing.T) {
s := lfHarness(t, settings.FileBrowserAdminGenerated, lfDriveA, lfDriveB)
const srcA = "https://files.example.hu/files/M%C3%A9dia%20&%20k%C3%B6nyvt%C3%A1r"
// R-1100: the button goes through the dashboard's pass (/__files/open); the FileBrowser path rides `to`.
const srcA = "/files/M%C3%A9dia%20&%20k%C3%B6nyvt%C3%A1r"
for _, c := range []struct{ slug, want string }{
{"romm", srcA + "/roms"},
{"calibre-web", srcA + "/media/books"},
{"plex", srcA + "/media"}, // the first drive's media folder
{"immich", ""},
} {
_, html, _ := lfGet(t, s, "/apps/"+c.slug)
m := lfFilesRe.FindStringSubmatch(html)
_, page, _ := lfGet(t, s, "/apps/"+c.slug)
m := lfFilesRe.FindStringSubmatch(page)
switch {
case c.want == "" && m != nil:
t.Errorf("%s: a Files button (%s) for an app with no folder", c.slug, m[1])
case c.want != "" && m == nil:
t.Errorf("%s: no Files button, want %s", c.slug, c.want)
case c.want != "" && m[1] != c.want:
t.Errorf("%s: Files → %s, want %s", c.slug, m[1], c.want)
case c.want != "" && lfOpenTo(m[1]) != c.want:
t.Errorf("%s: Files → %s (to=%q), want to=%s", c.slug, m[1], lfOpenTo(m[1]), c.want)
}
if strings.Contains(html, "M%C3%A9dia+") || strings.Contains(html, "%26") {
t.Errorf("%s: a link built with QueryEscape (+ for a space, %%26 for &)", c.slug)
for _, p := range lfOpenHrefs(page) {
if strings.Contains(p, "M%C3%A9dia+") || strings.Contains(p, "%26") {
t.Errorf("%s: a FileBrowser path built with QueryEscape (+ for a space, %%26 for &): %s", c.slug, p)
}
}
}
// The data tab lists each drive's media folders for Plex, and points at the file manager's login.
_, data, _ := lfGet(t, s, "/apps/plex/data")
for _, want := range []string{srcA + "/media/movies", srcA + "/media/tv",
"https://files.example.hu/files/HDD%202/media/movies", "https://files.example.hu/files/HDD%202/media/tv",
`data-fb-login-note="1"`, `href="/apps/filebrowser"`} {
for _, want := range []string{srcA + "/media/movies", srcA + "/media/tv", "/files/HDD%202/media/movies", "/files/HDD%202/media/tv"} {
if !lfHasOpen(data, want) {
t.Errorf("plex data tab lacks a link to %s (has %v)", want, lfOpenHrefs(data))
}
}
for _, want := range []string{`data-fb-login-note="1"`, `href="/apps/filebrowser"`} {
if !strings.Contains(data, want) {
t.Errorf("plex data tab lacks %s", want)
}
@@ -313,25 +349,30 @@ func TestAppPage_FilesButtonTarget(t *testing.T) {
// „romm links a folder on a drive that is not on the box".
func TestAppPage_UnknownSourceIsTextNotLink(t *testing.T) {
s := lfHarness(t, settings.FileBrowserAdminGenerated, lfDriveB) // drive A (romm's, plex's first) is away
_, html, _ := lfGet(t, s, "/apps/romm/data")
if strings.Contains(html, "/roms") && strings.Contains(html, "files.example.hu/files/M%C3%A9dia") {
t.Fatal("romm links a folder on a drive that is not on the box")
_, page, _ := lfGet(t, s, "/apps/romm/data")
for _, p := range lfOpenHrefs(page) {
if strings.HasPrefix(p, "/files/M%C3%A9dia") {
t.Fatal("romm links a folder on a drive that is not on the box")
}
}
if !strings.Contains(html, `data-folder-unavailable="1"`) {
if !strings.Contains(page, `data-folder-unavailable="1"`) {
t.Fatal("romm's folder is not shown as text when its source is absent")
}
if _, page, _ := lfGet(t, s, "/apps/romm"); lfFilesRe.MatchString(page) {
if _, p, _ := lfGet(t, s, "/apps/romm"); lfFilesRe.MatchString(p) {
t.Error("romm has a Files button although no folder of it is on the box")
}
// Plex: drive A text, drive B links; the button goes to the first PRESENT drive.
_, plex, _ := lfGet(t, s, "/apps/plex/data")
if !strings.Contains(plex, `data-folder-unavailable="1"`) || !strings.Contains(plex, "HDD%202/media/movies") {
if !strings.Contains(plex, `data-folder-unavailable="1"`) || !lfHasOpen(plex, "/files/HDD%202/media/movies") {
t.Error("plex: absent drive not text, or present drive not linked")
}
if strings.Contains(plex, "files.example.hu/files/M%C3%A9dia") {
t.Error("plex links the absent drive")
for _, p := range lfOpenHrefs(plex) {
if strings.HasPrefix(p, "/files/M%C3%A9dia") {
t.Error("plex links the absent drive")
}
}
if _, page, _ := lfGet(t, s, "/apps/plex"); !strings.Contains(page, `href="https://files.example.hu/files/HDD%202/media"`) {
_, pp, _ := lfGet(t, s, "/apps/plex")
if m := lfFilesRe.FindStringSubmatch(pp); m == nil || lfOpenTo(m[1]) != "/files/HDD%202/media" {
t.Error("plex Files button does not go to the first present drive")
}
}