82 lines
4.5 KiB
Go
82 lines
4.5 KiB
Go
package infra
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
|
)
|
|
|
|
const testSSOHeader = "X-Felhom-User-0123456789abcdef0123456789abcdef"
|
|
|
|
// With the header sign-in FileBrowser trusts THIS box's secret header name (never the old public X-Felhom-User), keeps
|
|
// password sign-in on (admin: the fallback and the operator's way), and sends a header user's logout to the dashboard.
|
|
// Without it the config is the pre-R-1100 one.
|
|
//
|
|
// COMPANION RED-PROOF (observed 2026-10-19): render renderFileBrowserAuth's header as "X-Felhom-User" → this fails with
|
|
// `the auth block lacks "header: \"X-Felhom-User-0123…\""`. Restored.
|
|
func TestFileBrowserConfig_R1100_TrustsOnlyTheBoxSecret(t *testing.T) {
|
|
sso := FileBrowserSSO{Header: testSSOHeader, LogoutURL: "https://felhom.example.hu/"}
|
|
c := RenderFileBrowserConfigKept([]settings.StoragePath{{Path: "/mnt/felhom-drives/hdd_1", Label: "HDD"}}, true, "", sso)
|
|
block := c[strings.Index(c, "\nauth:\n"):]
|
|
block = block[:strings.Index(block, "\nuserDefaults:")]
|
|
for _, want := range []string{" password:\n enabled: true", " proxy:\n enabled: true", `header: "` + testSSOHeader + `"`, `logoutRedirectUrl: "https://felhom.example.hu/"`} {
|
|
if !strings.Contains(block, want) {
|
|
t.Fatalf("the auth block lacks %q:\n%s", want, block)
|
|
}
|
|
}
|
|
if strings.Count(c, "header:") != 1 || strings.Contains(c, `header: "X-Felhom-User"`) {
|
|
t.Fatal("FileBrowser trusts a header name other than the box's secret")
|
|
}
|
|
plain := RenderFileBrowserConfigKept([]settings.StoragePath{{Path: "/mnt/felhom-drives/hdd_1", Label: "HDD"}}, true, "", FileBrowserSSO{})
|
|
if strings.Contains(plain, "auth:") || strings.Contains(plain, "X-Felhom-User") {
|
|
t.Fatal("without the header sign-in the config must carry no auth block")
|
|
}
|
|
}
|
|
|
|
// The household's header user is created by FileBrowser from userDefaults, so with the header sign-in the defaults are
|
|
// the household's file rights (not FileBrowser's admin or API); without it, the old download-only defaults.
|
|
//
|
|
// COMPANION RED-PROOF (observed 2026-10-19): return the download-only block for both cases → this fails with "the
|
|
// header user could not change files: modify false". Restored.
|
|
func TestFileBrowserConfig_R1100_HeaderUserGetsTheHouseholdsRights(t *testing.T) {
|
|
with := RenderFileBrowserConfigKept(nil, false, "", FileBrowserSSO{Header: testSSOHeader, LogoutURL: "https://felhom.example.hu/"})
|
|
perms := with[strings.Index(with, " permissions:\n"):strings.Index(with, "frontend:")]
|
|
for _, k := range []string{"modify", "share", "delete", "create", "download", "realtime"} {
|
|
if !strings.Contains(perms, " "+k+": true\n") {
|
|
t.Fatalf("the header user could not change files: %s false\n%s", k, perms)
|
|
}
|
|
}
|
|
for _, k := range []string{"admin", "api"} {
|
|
if !strings.Contains(perms, " "+k+": false\n") {
|
|
t.Fatalf("the header user must not get %s", k)
|
|
}
|
|
}
|
|
without := RenderFileBrowserConfigKept(nil, false, "", FileBrowserSSO{})
|
|
if !strings.Contains(without, " modify: false\n") || !strings.Contains(without, " download: true\n") {
|
|
t.Fatal("without the header sign-in the old download-only defaults must stay")
|
|
}
|
|
}
|
|
|
|
// The route asks the controller before FileBrowser and names the header in authResponseHeaders — which is what makes
|
|
// traefik remove a client's copy of it (measured, audits/proxyauth-spike-2026-10-18). Without the header sign-in: the
|
|
// old route, no middleware.
|
|
//
|
|
// COMPANION RED-PROOF (observed 2026-10-19): drop the router's `middlewares:` line in RenderFileBrowserRoute → this fails
|
|
// with "the files router does not ask the controller first". Restored.
|
|
func TestFileBrowserRoute_R1100_AsksTheControllerAndStripsTheHeader(t *testing.T) {
|
|
r := RenderFileBrowserRoute("example.hu", testSSOHeader)
|
|
router := r[strings.Index(r, " filebrowser:\n rule:"):strings.Index(r, " services:")]
|
|
if !strings.Contains(router, " middlewares:\n - "+FilesSSOMiddleware+"\n") {
|
|
t.Fatalf("the files router does not ask the controller first:\n%s", router)
|
|
}
|
|
for _, want := range []string{" " + FilesSSOMiddleware + ":\n forwardAuth:\n", `address: "` + FilesAuthURL + `"`, "authResponseHeaders:\n - \"" + testSSOHeader + "\""} {
|
|
if !strings.Contains(r, want) {
|
|
t.Fatalf("the route lacks %q:\n%s", want, r)
|
|
}
|
|
}
|
|
if plain := RenderFileBrowserRoute("example.hu", ""); strings.Contains(plain, "middlewares") || strings.Contains(plain, "X-Felhom-User") {
|
|
t.Fatalf("without the header sign-in the route must be the old one:\n%s", plain)
|
|
}
|
|
}
|