179 lines
7.7 KiB
Go
179 lines
7.7 KiB
Go
package main
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestGatewayForwardsOnlyCreatorAllowlist(t *testing.T) {
|
|
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/v1/creator/tasks/task-1" || r.URL.RawQuery != "session_id=session-1&workspace_id=workspace-1" {
|
|
t.Fatalf("unexpected upstream target %s?%s", r.URL.Path, r.URL.RawQuery)
|
|
}
|
|
if r.Header.Get("Authorization") != "Bearer bounded-session" {
|
|
t.Fatal("authorization header was not forwarded")
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = io.WriteString(w, `{"TaskID":"task-1"}`)
|
|
}))
|
|
defer upstream.Close()
|
|
handler, err := newHandler(upstream.URL, upstream.URL, upstream.URL, upstream.Client())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := httptest.NewRequest(http.MethodGet, "/api/tasks/task-1?session_id=session-1&workspace_id=workspace-1", nil)
|
|
request.Header.Set("Authorization", "Bearer bounded-session")
|
|
response := httptest.NewRecorder()
|
|
handler.ServeHTTP(response, request)
|
|
if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), "task-1") {
|
|
t.Fatalf("response = %d %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestGatewayForwardsCreatorCredentialsOnlyInBody(t *testing.T) {
|
|
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/v1/creator/sessions" || r.Method != http.MethodPost {
|
|
t.Fatalf("unexpected session target %s %s", r.Method, r.URL.Path)
|
|
}
|
|
if r.Header.Get("Authorization") != "" || r.Header.Get("Content-Type") != "application/json" {
|
|
t.Fatalf("unexpected session headers Authorization=%q Content-Type=%q", r.Header.Get("Authorization"), r.Header.Get("Content-Type"))
|
|
}
|
|
body, _ := io.ReadAll(r.Body)
|
|
if string(body) != `{"username":"m","password":"secret"}` {
|
|
t.Fatalf("session body = %s", body)
|
|
}
|
|
_, _ = io.WriteString(w, `{"Token":"bounded"}`)
|
|
}))
|
|
defer upstream.Close()
|
|
handler, err := newHandler(upstream.URL, upstream.URL, upstream.URL, upstream.Client())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := httptest.NewRequest(http.MethodPost, "/api/session", strings.NewReader(`{"username":"m","password":"secret"}`))
|
|
request.Header.Set("Content-Type", "application/json")
|
|
response := httptest.NewRecorder()
|
|
handler.ServeHTTP(response, request)
|
|
if response.Code != http.StatusOK {
|
|
t.Fatalf("session response = %d %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestGatewayDeniesInternalAndMutationRoutes(t *testing.T) {
|
|
handler, err := newHandler("http://core-runtime:8080", "http://continuity-runtime:8081", "http://commit-runtime:8082", http.DefaultClient)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, test := range []struct{ method, path string }{
|
|
{http.MethodPost, "/api/tasks/task-1"},
|
|
{http.MethodGet, "/api/principal-bindings/actor/principal"},
|
|
{http.MethodPost, "/api/focus-task-commits"},
|
|
{http.MethodPost, "/api/prospective-items/item-1"},
|
|
{http.MethodGet, "/api/tasks/../../healthz"},
|
|
{http.MethodPost, "/api/actions/arbitrary"},
|
|
{http.MethodGet, "/api/actions/satisfy-delivered"},
|
|
} {
|
|
request := httptest.NewRequest(test.method, test.path, nil)
|
|
response := httptest.NewRecorder()
|
|
handler.ServeHTTP(response, request)
|
|
if response.Code != http.StatusNotFound {
|
|
t.Fatalf("%s %s returned %d", test.method, test.path, response.Code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGatewayRoutesProspectiveItemToContinuity(t *testing.T) {
|
|
continuity := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/v1/creator/prospective-items/item-1" {
|
|
t.Fatalf("unexpected Continuity target %s", r.URL.Path)
|
|
}
|
|
if r.Header.Get("Authorization") != "Bearer bounded-session" {
|
|
t.Fatal("authorization header was not forwarded")
|
|
}
|
|
_, _ = io.WriteString(w, `{"ID":"item-1"}`)
|
|
}))
|
|
defer continuity.Close()
|
|
handler, err := newHandler("http://core-runtime.invalid", continuity.URL, "http://commit-runtime.invalid", continuity.Client())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := httptest.NewRequest(http.MethodGet, "/api/prospective-items/item-1", nil)
|
|
request.Header.Set("Authorization", "Bearer bounded-session")
|
|
response := httptest.NewRecorder()
|
|
handler.ServeHTTP(response, request)
|
|
if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), "item-1") {
|
|
t.Fatalf("response = %d %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestGatewayRoutesProspectiveListToContinuity(t *testing.T) {
|
|
continuity := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/v1/creator/prospective-items" || r.URL.RawQuery != "limit=25" {
|
|
t.Fatalf("unexpected Continuity list target %s?%s", r.URL.Path, r.URL.RawQuery)
|
|
}
|
|
_, _ = io.WriteString(w, `{"Items":[]}`)
|
|
}))
|
|
defer continuity.Close()
|
|
handler, err := newHandler("http://core-runtime.invalid", continuity.URL, "http://commit-runtime.invalid", continuity.Client())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := httptest.NewRequest(http.MethodGet, "/api/prospective-items?limit=25", nil)
|
|
response := httptest.NewRecorder()
|
|
handler.ServeHTTP(response, request)
|
|
if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), "Items") {
|
|
t.Fatalf("response = %d %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestGatewayRoutesOnlyBoundedCreatorCommandsToCommit(t *testing.T) {
|
|
commitRuntime := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/v1/creator/continuity/satisfy-delivered" || r.Method != http.MethodPost {
|
|
t.Fatalf("unexpected Commit target %s %s", r.Method, r.URL.Path)
|
|
}
|
|
if r.Header.Get("Authorization") != "Bearer bounded-session" {
|
|
t.Fatal("authorization header was not forwarded")
|
|
}
|
|
body, _ := io.ReadAll(r.Body)
|
|
if !strings.Contains(string(body), "prospective-1") || strings.Contains(string(body), "GrantID") || strings.Contains(string(body), "CapabilityID") {
|
|
t.Fatalf("unexpected command body %s", body)
|
|
}
|
|
w.WriteHeader(http.StatusCreated)
|
|
_, _ = io.WriteString(w, `{"State":"SUCCEEDED"}`)
|
|
}))
|
|
defer commitRuntime.Close()
|
|
handler, err := newHandler("http://core-runtime.invalid", "http://continuity-runtime.invalid", commitRuntime.URL, commitRuntime.Client())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := httptest.NewRequest(http.MethodPost, "/api/actions/satisfy-delivered", strings.NewReader(`{"ProspectiveItemID":"prospective-1"}`))
|
|
request.Header.Set("Authorization", "Bearer bounded-session")
|
|
request.Header.Set("Content-Type", "application/json")
|
|
response := httptest.NewRecorder()
|
|
handler.ServeHTTP(response, request)
|
|
if response.Code != http.StatusCreated || !strings.Contains(response.Body.String(), "SUCCEEDED") {
|
|
t.Fatalf("response = %d %s", response.Code, response.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestConsoleAndSecurityHeaders(t *testing.T) {
|
|
handler, err := newHandler("http://core-runtime:8080", "http://continuity-runtime:8081", "http://commit-runtime:8082", http.DefaultClient)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
response := httptest.NewRecorder()
|
|
handler.ServeHTTP(response, request)
|
|
if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), "Домовой") ||
|
|
!strings.Contains(response.Body.String(), "satisfaction-dialog") || !strings.Contains(response.Body.String(), "Подтверждаю переход") ||
|
|
!strings.Contains(response.Body.String(), "creator-username") || !strings.Contains(response.Body.String(), "creator-password") ||
|
|
strings.Contains(response.Body.String(), "bootstrap-token") {
|
|
t.Fatalf("console response = %d", response.Code)
|
|
}
|
|
if response.Header().Get("Content-Security-Policy") == "" {
|
|
t.Fatal("content security policy is missing")
|
|
}
|
|
}
|