package hammerhead import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) func keyCall(t *testing.T, s *Server, key, method, path, body string) (int, map[string]any) { t.Helper() req := httptest.NewRequest(method, path, strings.NewReader(body)) if key != "" { req.Header.Set("Authorization", "Bearer "+key) } rec := httptest.NewRecorder() s.Handler().ServeHTTP(rec, req) var out map[string]any json.Unmarshal(rec.Body.Bytes(), &out) return rec.Code, out } func mintKey(t *testing.T, s *Server, org string) string { t.Helper() k := "hhk_" + randHex(4) + "_" + randHex(24) parts := strings.SplitN(k, "_", 3) s.mu.Lock() s.st.APIKeys = append(s.st.APIKeys, &APIKey{ID: parts[1], OrgID: org, Name: "t", Hash: sha(parts[2])}) s.mu.Unlock() return k } func TestServerKeysManageEdgesAndOrgs(t *testing.T) { f := newFx(t) server := mintKey(t, f.s, "") orgKey := mintKey(t, f.s, f.org) if c, _ := keyCall(t, f.s, server, "POST", "/api/edges", `{"name":"e2","tunnelAddr":"1.2.3.4:51820"}`); c != 200 { t.Fatalf("server key should create an edge, got %d", c) } if c, _ := keyCall(t, f.s, orgKey, "POST", "/api/edges", `{"name":"e3","tunnelAddr":"1.2.3.4:51820"}`); c != 401 { t.Fatalf("org key must not create edges, got %d", c) } if c, _ := keyCall(t, f.s, server, "POST", "/api/orgs", `{"name":"Second"}`); c != 200 { t.Fatalf("server key should create an org, got %d", c) } // server key reaches any org's endpoints; an org key only its own if c, _ := keyCall(t, f.s, server, "POST", "/api/orgs/"+f.org+"/sites", `{"name":"x"}`); c != 200 { t.Fatalf("server key on org: %d", c) } if c, _ := keyCall(t, f.s, orgKey, "POST", "/api/orgs/nope/sites", `{"name":"x"}`); c != 404 { t.Fatalf("org key on other org: %d", c) } // server alert channels if c, _ := keyCall(t, f.s, server, "GET", "/api/admin/alerts", ""); c != 200 { t.Fatalf("server key alerts: %d", c) } // keys can't mint keys at server level if c, _ := keyCall(t, f.s, server, "POST", "/api/admin/apikeys", `{"name":"k"}`); c != 401 { t.Fatalf("server key must not mint server keys, got %d", c) } if c, _ := keyCall(t, f.s, "", "GET", "/api/orgs", ""); c != 401 { t.Fatalf("anonymous list orgs: %d", c) } req := httptest.NewRequest("GET", "/api/orgs", nil) req.Header.Set("Authorization", "Bearer "+orgKey) rec := httptest.NewRecorder() f.s.Handler().ServeHTTP(rec, req) var orgs []Org json.Unmarshal(rec.Body.Bytes(), &orgs) if rec.Code != http.StatusOK || len(orgs) != 1 || orgs[0].ID != f.org { t.Fatalf("org key should list only its org: %d %s", rec.Code, rec.Body) } // org key lists edges its org may use req = httptest.NewRequest("GET", "/api/edges", nil) req.Header.Set("Authorization", "Bearer "+orgKey) rec = httptest.NewRecorder() f.s.Handler().ServeHTTP(rec, req) if rec.Code != 200 || !strings.Contains(rec.Body.String(), "edge-1") { t.Fatalf("org key edges: %d %s", rec.Code, rec.Body) } }