package api import ( "net/http" "net/http/httptest" "strings" "testing" "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) // R-879 at the endpoints the boxes call: with the secrets sealed, (1) the agent's host-report and the // controller's event auth still accept the right key and refuse a wrong one; (2) with a WRONG sealing key // the boxes still authenticate, while every path that would serve or compare a sealed value answers 500 — // never an empty key, never "wrong password" for a right one. func r879Get(h *Handler, path, pw string) *httptest.ResponseRecorder { req := httptest.NewRequest(http.MethodGet, "/api/v1"+path, nil) req.Header.Set("X-Retrieval-Password", pw) rr := httptest.NewRecorder() h.ServeHTTP(rr, req) return rr } func TestR879_EndpointsWithSealedSecrets(t *testing.T) { h, st, _ := newTestHandler(t) if err := st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", APIKey: "ckey-r879", RetrievalPassword: "owner-pass"}); err != nil { t.Fatal(err) } if err := st.UpsertHost(&store.Host{HostID: "h1", CustomerID: "c1", APIKey: "HKEY-r879"}); err != nil { t.Fatal(err) } if rr := do(h, http.MethodPost, "/host-report", "HKEY-r879", validReportBody("h1")); rr.Code != 200 { t.Fatalf("host-report with the right key = %d %s", rr.Code, rr.Body.String()) } if rr := do(h, http.MethodPost, "/host-report", "HKEY-wrong", validReportBody("h1")); rr.Code != http.StatusUnauthorized { t.Fatalf("host-report with a wrong key = %d, want 401", rr.Code) } if _, isGlobal, ok := h.checkAuthCustomer(bearerReq("ckey-r879")); !ok || isGlobal { t.Fatal("controller key no longer authenticates") } if _, _, ok := h.checkAuthCustomer(bearerReq("ckey-wrong")); ok { t.Fatal("a wrong controller key authenticated") } // The passphrase compare still sees the opened value (503 = past the compare, no template here). if rr := r879Get(h, "/config/c1", "owner-pass"); rr.Code != http.StatusServiceUnavailable { t.Fatalf("config retrieve with the right passphrase = %d, want 503 (compare passed)", rr.Code) } if rr := r879Get(h, "/config/c1", "nope"); rr.Code != http.StatusUnauthorized { t.Fatalf("config retrieve with a wrong passphrase = %d, want 401", rr.Code) } // Re-enroll re-serves the opened host key. if rr := doEnroll(h, "c1", "owner-pass"); rr.Code != 200 || !strings.Contains(rr.Body.String(), "HKEY-r879") { t.Fatalf("re-enroll = %d %s, want the existing key", rr.Code, rr.Body.String()) } // The hub now runs with a WRONG sealing key. if err := st.SetOffsiteSecretKey([]byte("another-key-of-exactly-32-bytes!")); err != nil { t.Fatal(err) } if rr := do(h, http.MethodPost, "/host-report", "HKEY-r879", validReportBody("h1")); rr.Code != 200 { t.Fatalf("with a wrong sealing key the agent is locked out: %d", rr.Code) } if _, _, ok := h.checkAuthCustomer(bearerReq("ckey-r879")); !ok { t.Fatal("with a wrong sealing key the controller is locked out") } for _, p := range []string{"/config/c1", "/recovery/c1", "/artifacts/c1"} { if rr := r879Get(h, p, "owner-pass"); rr.Code != http.StatusInternalServerError { t.Errorf("%s with an unopenable passphrase = %d, want 500", p, rr.Code) } } rr := doEnroll(h, "c1", "owner-pass") if rr.Code != http.StatusInternalServerError || strings.Contains(rr.Body.String(), "api_key") { t.Fatalf("re-enroll with an unopenable key = %d %s, want 500 and no key", rr.Code, rr.Body.String()) } } func bearerReq(tok string) *http.Request { req := httptest.NewRequest(http.MethodPost, "/api/v1/event", nil) req.Header.Set("Authorization", "Bearer "+tok) return req }