improvements for first-time setup

This commit is contained in:
onysd 2026-09-08 17:56:26 +03:00
parent e59d85cf57
commit 979d27ec7a
16 changed files with 901 additions and 33 deletions

View file

@ -2,10 +2,14 @@ package main
import (
"errors"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/crypto/bcrypt"
"telesrv/internal/identity"
)
func TestValidateAdminPassword(t *testing.T) {
@ -203,6 +207,57 @@ func TestBreakGlassUsernameIsCaseInsensitive(t *testing.T) {
}
}
// The break-glass password quickstart generates for the very first login
// must stop authenticating once the first-run wizard is done, but a
// password an operator actually chose -- even one that happens to still be
// sitting in .env from before the wizard finished -- must never be
// affected by that. This is the actual integration point between
// validSecret and identity.Store; the package's own tests cover
// SetupPending/TemporaryPasswordMatches in isolation.
func TestValidSecretRetiresOnlyTheGeneratedPassword(t *testing.T) {
dir := t.TempDir()
store := identity.NewStore(dir)
s := &server{cfg: uiConfig{Password: "generated-once", Permissions: []string{permissionAll}}, identity: store}
// No marker written at all yet (identity.Store's zero state) -- the
// password behaves like an ordinary one an operator set.
if !s.validSecret("generated-once") {
t.Fatal("password should authenticate before any wizard marker exists")
}
// Bootstrap-style: the setup-pending marker plus the matching
// temporary-password marker, exactly as tui-panel/server-panel.py's
// bootstrap_env() writes them on a fresh install.
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, ".setup_pending"), nil, 0o644); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, ".admin_password_temporary"), []byte("generated-once"), 0o644); err != nil {
t.Fatal(err)
}
if !s.validSecret("generated-once") {
t.Fatal("the generated password must keep working while the wizard is still pending")
}
// Wizard finishes: MarkSetupComplete removes both markers.
if err := store.MarkSetupComplete(); err != nil {
t.Fatal(err)
}
if s.validSecret("generated-once") {
t.Fatal("the generated password must stop authenticating once setup is complete")
}
// An operator-chosen password behaves normally regardless: setting a
// new .env value (this test's stand-in for that) authenticates whether
// or not a wizard ever ran, because it never matches either marker.
s.cfg.Password = "an-operator-actually-chose-this"
if !s.validSecret("an-operator-actually-chose-this") {
t.Fatal("an operator-chosen password must authenticate after setup completion, same as always")
}
}
// A session for a named account must not be trusted on the strength of its
// signature alone: the account's rights are re-read per request, and a nil read
// store has to fail closed rather than fall back to the claims.