From b70dfdb80d897abf77b74a78eb59b984dfba64d4 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Wed, 20 May 2026 14:08:43 +0300 Subject: Block bootstrap.html access after first user created (o9) serveBootstrap now calls CountUsers() and redirects to /login.html when the user count is non-zero, preventing the bootstrap form from being reachable on an already-configured instance. Co-Authored-By: Claude Opus 4.7 --- player-server/internal/api/handlers_test.go | 30 ++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'player-server/internal/api/handlers_test.go') diff --git a/player-server/internal/api/handlers_test.go b/player-server/internal/api/handlers_test.go index 5fbd4c9..e031f40 100644 --- a/player-server/internal/api/handlers_test.go +++ b/player-server/internal/api/handlers_test.go @@ -433,14 +433,42 @@ func TestServer_StaticPages(t *testing.T) { } }) - t.Run("bootstrap public", func(t *testing.T) { + // After bootstrap is complete (users exist) the bootstrap page must + // redirect to /login.html so the form is no longer reachable. + t.Run("bootstrap redirects when users exist", func(t *testing.T) { srv := newTestServer(t, store, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) req := httptest.NewRequest(http.MethodGet, "/bootstrap.html", nil) rr := httptest.NewRecorder() srv.ServeHTTP(rr, req) + if rr.Code != http.StatusTemporaryRedirect { + t.Fatalf("expected %d, got %d", http.StatusTemporaryRedirect, rr.Code) + } + if loc := rr.Header().Get("Location"); loc != "/login.html" { + t.Fatalf("expected redirect to /login.html, got %q", loc) + } + }) + + // Before bootstrap the page must be publicly accessible (no users yet). + t.Run("bootstrap accessible when no users", func(t *testing.T) { + noUserStore := &repository.MockStore{ + UserRepo: repository.MockUserRepo{ + CountUsersFunc: func(ctx context.Context) (int, error) { return 0, nil }, + }, + } + noUserAuthSvc := &service.MockAuthService{ + CountUsersFunc: func(context.Context) (int, error) { return 0, nil }, + GetUserByIDFunc: func(context.Context, int64) (*model.User, error) { return &model.User{ID: 1, IsAdmin: true}, nil }, + } + srv := newTestServer(t, noUserStore, nil, nil, cfg, nil, nil, nil, nil, nil, nil, nil, nil, noUserAuthSvc, nil) + req := httptest.NewRequest(http.MethodGet, "/bootstrap.html", nil) + rr := httptest.NewRecorder() + srv.ServeHTTP(rr, req) if rr.Code != http.StatusOK { t.Fatalf("expected %d, got %d", http.StatusOK, rr.Code) } + if !strings.Contains(rr.Body.String(), "bootstrap") { + t.Fatal("expected bootstrap page body") + } }) t.Run("css public", func(t *testing.T) { -- cgit v1.2.3