v0.321.0: FileBrowser's stale password and its own banners; one backup section on the Data tab; install address of an installed app
gates / gates (push) Successful in 43s

R-1101: the change-it line is gone for FileBrowser; in the generated state the page asks FileBrowser whether
the stored password still logs in (only its own 401/403 counts) and, refused, says so and offers the
new-password press. The rendered FileBrowser config switches off its update notice and its Install-app
prompt. The Data tab's backup part is one section in plain words (no off-drive, no PBS); the export
password eye is a text button. R-1095: /apps/{slug}/install of an installed app 302s to its settings tab.

Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-16 17:17:26 +02:00
parent c02c9d833a
commit 7e791d9b5d
30 changed files with 1262 additions and 363 deletions
@@ -141,3 +141,44 @@ func TestEffectiveLoginKind_DefaultAndUnknown(t *testing.T) {
}
}
}
// R-1101 (v0.321.0): the stored password is called refused ONLY on FileBrowser's own 401.
// RED-PROOF (2026-10-16, REPORT): with `refused := err == nil && (...)` replaced by `refused := code != http.StatusOK`,
// this failed at „FileBrowser down reads as refused".
func TestFileBrowserStoredPassword_RefusedOnlyOnA401(t *testing.T) {
fake := &fakeFB{password: "TESTONLY-stored-1"}
srv := httptest.NewServer(fake.handler())
m, st, key := fbManager(t, srv.URL)
enc, err := crypto.Encrypt(key, "TESTONLY-stored-1")
if err != nil {
t.Fatal(err)
}
if err := st.SetFileBrowserAdmin(settings.FileBrowserAdminGenerated, enc, "2026-10-16T00:00:00Z"); err != nil {
t.Fatal(err)
}
if m.FileBrowserStoredPasswordRefused() {
t.Fatal("the stored password still logs in, and it reads as refused")
}
fake.mu.Lock()
fake.password = "TESTONLY-changed-in-filebrowser"
fake.mu.Unlock()
if !m.FileBrowserStoredPasswordRefused() {
t.Fatal("FileBrowser refuses the stored password (401), and it does not read as refused")
}
// The reset is then offered in the generated state, and replaces it.
calls := 0
m.fbSetPassword = func(_ context.Context, pw string) error {
calls++
fake.mu.Lock()
fake.password = pw
fake.mu.Unlock()
return nil
}
if err := m.ResetFileBrowserAdminPassword(context.Background()); err != nil || calls != 1 {
t.Fatalf("reset with a refused stored password: err %v, apply ran %d times", err, calls)
}
srv.Close()
if m.FileBrowserStoredPasswordRefused() {
t.Fatal("FileBrowser down reads as refused — an unproven refusal must not tell the household its password changed")
}
}