v0.326.0: FileBrowser's default password is checked at every FileBrowser start (R-977)
gates / gates (push) Successful in 49s
gates / gates (push) Successful in 49s
The probe ran once per box; a re-created FileBrowser data volume came back on admin/admin and was never probed again (measured on scratch 9202 with 0.324.0). Now once per FileBrowser run (its StartedAt): the default found -> a generated password, verified and stored encrypted; any other password -> nothing changes. TestFileBrowserAdmin_R977_DefaultBackOnANewRunIsReplaced, red-proved. The comment "before a claim there is no tunnel" was false and is corrected; the tunnel is not changed. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -145,3 +145,68 @@ func TestFileBrowserAdmin_UnreachableRecordsNothing(t *testing.T) {
|
||||
t.Fatalf("an unreachable FileBrowser recorded a decision: %q", state)
|
||||
}
|
||||
}
|
||||
|
||||
// R-977: the decision is no longer once per box. A FileBrowser whose data was re-created (a new run whose database
|
||||
// starts on admin/admin again) is reset at its first tick; the same run is never probed twice; a new run on another
|
||||
// password (the household's own) leaves the recorded decision alone.
|
||||
//
|
||||
// RED-PROOF (2026-10-17): with the old early return (`if state != "" { return nil }`) the second run keeps
|
||||
// admin/admin and this fails at „the default login came back on a new run and was left".
|
||||
func TestFileBrowserAdmin_R977_DefaultBackOnANewRunIsReplaced(t *testing.T) {
|
||||
fb := &fakeFB{password: "admin"}
|
||||
srv := httptest.NewServer(fb.handler())
|
||||
defer srv.Close()
|
||||
m, st, key := fbManager(t, srv.URL)
|
||||
run := "2026-10-17T06:54:56Z"
|
||||
m.fbStarted = func() string { return run }
|
||||
|
||||
if err := m.EnsureFileBrowserAdminPassword(); err != nil { // first run: a new box
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, enc1, _ := st.GetFileBrowserAdmin()
|
||||
|
||||
// The data volume is re-created: FileBrowser starts again on its default.
|
||||
fb.mu.Lock()
|
||||
fb.password = "admin"
|
||||
fb.mu.Unlock()
|
||||
if err := m.EnsureFileBrowserAdminPassword(); err != nil { // the SAME run: not probed again
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fb.puts != 1 {
|
||||
t.Fatalf("the same FileBrowser run was probed and changed again (puts=%d)", fb.puts)
|
||||
}
|
||||
run = "2026-10-17T09:12:00Z" // the box brought FileBrowser back: a new run
|
||||
if err := m.EnsureFileBrowserAdminPassword(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, c, _ := fbLogin(srv.Client().Do, srv.URL, "admin"); c == http.StatusOK {
|
||||
t.Fatal("the default login came back on a new run and was left")
|
||||
}
|
||||
state, enc2, _ := st.GetFileBrowserAdmin()
|
||||
if state != settings.FileBrowserAdminGenerated || enc2 == "" || enc2 == enc1 {
|
||||
t.Fatalf("a new generated password must be recorded: state=%q changed=%v", state, enc2 != enc1)
|
||||
}
|
||||
pw, err := crypto.Decrypt(key, enc2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, c, _ := fbLogin(srv.Client().Do, srv.URL, pw); c != http.StatusOK {
|
||||
t.Fatalf("the recorded password does not log in (HTTP %d)", c)
|
||||
}
|
||||
|
||||
// The household changes it in FileBrowser; a later run on THEIR password changes nothing.
|
||||
fb.mu.Lock()
|
||||
fb.password = "the-households-own"
|
||||
fb.mu.Unlock()
|
||||
run = "2026-10-18T02:30:00Z"
|
||||
puts := fb.puts
|
||||
if err := m.EnsureFileBrowserAdminPassword(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fb.puts != puts || fb.password != "the-households-own" {
|
||||
t.Fatalf("a run on the household's own password was changed (puts %d -> %d)", puts, fb.puts)
|
||||
}
|
||||
if s, e, _ := st.GetFileBrowserAdmin(); s != settings.FileBrowserAdminGenerated || e != enc2 {
|
||||
t.Fatalf("the recorded decision moved on a run that is not on the default: state=%q", s)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user