843b319f35
gates / gates (push) Successful in 14s
MinAgent: 0.131.0 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
97 lines
3.3 KiB
Go
97 lines
3.3 KiB
Go
package web
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/crypto"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
|
|
)
|
|
|
|
// R-513 — the file manager's login is shown where app logins are, under the R-254 rule: the value is
|
|
// never in the page, only behind the reveal act.
|
|
|
|
const fbTestPW = "TESTONLYfbPw7Kq2"
|
|
|
|
func renderFBPage(t *testing.T, operatorSet bool) string {
|
|
t.Helper()
|
|
s := securityHarness(t)
|
|
s.loadTemplates()
|
|
data := map[string]interface{}{
|
|
"Page": "stacks", "Title": "FileBrowser", "Domain": "example.hu",
|
|
"Stack": stacks.Stack{Name: "filebrowser", Deployed: true, State: "running"},
|
|
"Meta": stacks.Metadata{DisplayName: "FileBrowser", Slug: "filebrowser"},
|
|
"HasAppInfo": true,
|
|
"InitialCreds": &stacks.ExtractedCreds{Available: true, Username: "admin"},
|
|
}
|
|
if operatorSet {
|
|
data["InitialCredsOperatorSet"] = true
|
|
} else {
|
|
data["InitialCredsHasPassword"] = true
|
|
}
|
|
var buf bytes.Buffer
|
|
if err := s.tmpl.ExecuteTemplate(&buf, "app_info", data); err != nil {
|
|
t.Fatalf("render: %v", err)
|
|
}
|
|
return buf.String()
|
|
}
|
|
|
|
func TestFileBrowserLoginCard_Generated(t *testing.T) {
|
|
html := renderFBPage(t, false)
|
|
if !strings.Contains(html, "initial-credentials/reveal") || !strings.Contains(html, "admin") {
|
|
t.Fatal("generated state: the login card has no reveal call or no username")
|
|
}
|
|
if strings.Contains(html, "zemeltet") { // „üzemeltető" — ASCII fragment
|
|
t.Fatal("generated state shows the operator-set text")
|
|
}
|
|
}
|
|
|
|
// RED-PROOF (run 2026-09-15, recorded in REPORT.md): with the {{if .InitialCredsOperatorSet}} branch
|
|
// removed from app_info.html, the operator page rendered the reveal button and this failed at
|
|
// "operator state still offers a reveal".
|
|
func TestFileBrowserLoginCard_OperatorSet(t *testing.T) {
|
|
html := renderFBPage(t, true)
|
|
if !strings.Contains(html, "zemeltet") {
|
|
t.Fatal("operator state does not say the operator set the password")
|
|
}
|
|
if strings.Contains(html, `id="initcred-reveal"`) {
|
|
t.Fatal("operator state still offers a reveal for a password the Felhom does not hold")
|
|
}
|
|
}
|
|
|
|
func revealFB(t *testing.T, s *Server) (int, map[string]any) {
|
|
t.Helper()
|
|
w := httptest.NewRecorder()
|
|
s.fileBrowserPasswordReveal(w, httptest.NewRequest(http.MethodPost, "/apps/filebrowser/initial-credentials/reveal", nil))
|
|
var body map[string]any
|
|
_ = json.Unmarshal(w.Body.Bytes(), &body)
|
|
return w.Code, body
|
|
}
|
|
|
|
func TestFileBrowserPasswordReveal(t *testing.T) {
|
|
s := securityHarness(t)
|
|
s.encKey = bytes.Repeat([]byte{7}, 32)
|
|
if code, _ := revealFB(t, s); code != http.StatusNotFound {
|
|
t.Fatalf("undecided: want 404, got %d", code)
|
|
}
|
|
_ = s.settings.SetFileBrowserAdmin(settings.FileBrowserAdminOperator, "", "2026-09-15T00:00:00Z")
|
|
if code, _ := revealFB(t, s); code != http.StatusNotFound {
|
|
t.Fatalf("operator-set: want 404, got %d", code)
|
|
}
|
|
enc, err := crypto.Encrypt(s.encKey, fbTestPW)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = s.settings.SetFileBrowserAdmin(settings.FileBrowserAdminGenerated, enc, "2026-09-15T00:00:00Z")
|
|
code, body := revealFB(t, s)
|
|
data, _ := body["data"].(map[string]any)
|
|
if code != http.StatusOK || data["password"] != fbTestPW {
|
|
t.Fatalf("generated: want 200 + the decrypted password, got %d %v", code, body)
|
|
}
|
|
}
|