package main import ( "encoding/json" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "time" ) func TestBearerTokenRequiresBearerScheme(t *testing.T) { request := httptest.NewRequest("GET", "/", nil) request.Header.Set("Authorization", "Basic value") if token := bearerToken(request); token != "" { t.Fatalf("unexpected token %q", token) } request.Header.Set("Authorization", "Bearer creator-session") if token := bearerToken(request); token != "creator-session" { t.Fatalf("token = %q", token) } } func TestCreatorSessionHandlerAcceptsConfiguredCredentials(t *testing.T) { handler := creatorSessionHandler("recovery", "m", "password", []byte("01234567890123456789012345678901"), func() time.Time { return time.Unix(1000, 0) }) request := httptest.NewRequest(http.MethodPost, "/v1/creator/sessions", strings.NewReader(`{"username":"m","password":"password"}`)) response := httptest.NewRecorder() handler(response, request) if response.Code != http.StatusOK { t.Fatalf("status = %d body=%s", response.Code, response.Body.String()) } var issued struct { Token string } if err := json.NewDecoder(response.Body).Decode(&issued); err != nil || issued.Token == "" { t.Fatalf("session response = %+v, err=%v", issued, err) } } func TestCreatorSessionHandlerFailsClosed(t *testing.T) { handler := creatorSessionHandler("recovery", "m", "password", []byte("01234567890123456789012345678901"), time.Now) tests := []string{ `{"username":"wrong","password":"password"}`, `{"username":"m","password":"wrong"}`, `{"username":"","password":""}`, `{"username":"m","password":"password","actor":"attacker"}`, `{`, `{"username":"m","password":"password"}{}`, } for _, body := range tests { request := httptest.NewRequest(http.MethodPost, "/v1/creator/sessions", strings.NewReader(body)) response := httptest.NewRecorder() handler(response, request) if response.Code != http.StatusUnauthorized || strings.TrimSpace(response.Body.String()) != "creator authentication denied" { t.Fatalf("body %q: status=%d response=%q", body, response.Code, response.Body.String()) } } } func TestCreatorSessionHandlerPreservesBootstrapRecovery(t *testing.T) { handler := creatorSessionHandler("recovery", "", "", []byte("01234567890123456789012345678901"), time.Now) request := httptest.NewRequest(http.MethodPost, "/v1/creator/sessions", nil) request.Header.Set("Authorization", "Bearer recovery") response := httptest.NewRecorder() handler(response, request) if response.Code != http.StatusOK { t.Fatalf("bootstrap recovery status = %d", response.Code) } } func TestReadRequiredSecretFromEnvironment(t *testing.T) { t.Setenv("TEST_CREATOR_SECRET", "direct-value") value, err := readRequiredSecret("TEST_CREATOR_SECRET") if err != nil || value != "direct-value" { t.Fatalf("value=%q err=%v", value, err) } } func TestReadRequiredSecretFromFile(t *testing.T) { fileName := filepath.Join(t.TempDir(), "secret") if err := os.WriteFile(fileName, []byte("file-value\r\n"), 0o600); err != nil { t.Fatal(err) } t.Setenv("TEST_CREATOR_SECRET_FILE", fileName) value, err := readRequiredSecret("TEST_CREATOR_SECRET") if err != nil || value != "file-value" { t.Fatalf("value=%q err=%v", value, err) } } func TestReadRequiredSecretFailsClosed(t *testing.T) { t.Run("both sources", func(t *testing.T) { t.Setenv("TEST_CREATOR_SECRET", "direct") t.Setenv("TEST_CREATOR_SECRET_FILE", filepath.Join(t.TempDir(), "secret")) if _, err := readRequiredSecret("TEST_CREATOR_SECRET"); err == nil { t.Fatal("expected ambiguous configuration to fail") } }) t.Run("empty file", func(t *testing.T) { fileName := filepath.Join(t.TempDir(), "secret") if err := os.WriteFile(fileName, nil, 0o600); err != nil { t.Fatal(err) } t.Setenv("TEST_CREATOR_SECRET_FILE", fileName) if _, err := readRequiredSecret("TEST_CREATOR_SECRET"); err == nil { t.Fatal("expected empty secret to fail") } }) t.Run("multiline file", func(t *testing.T) { fileName := filepath.Join(t.TempDir(), "secret") if err := os.WriteFile(fileName, []byte("first\nsecond\n"), 0o600); err != nil { t.Fatal(err) } t.Setenv("TEST_CREATOR_SECRET_FILE", fileName) if _, err := readRequiredSecret("TEST_CREATOR_SECRET"); err == nil { t.Fatal("expected multiline secret to fail") } }) t.Run("missing file", func(t *testing.T) { t.Setenv("TEST_CREATOR_SECRET_FILE", filepath.Join(t.TempDir(), "missing")) if _, err := readRequiredSecret("TEST_CREATOR_SECRET"); err == nil { t.Fatal("expected missing secret file to fail") } }) }