Improve gateway auth middleware to use internal auth context

Enforce GATEWAY_API_KEY in Makefile E2E test target Fix gateway E2E test
payload reading to use io.ReadAll Remove deprecated multi-node test
targets and cleanup Makefile comments
This commit is contained in:
anonpenguin 2025-08-23 11:14:11 +03:00
parent 895f1d5dff
commit 03b3b38967
3 changed files with 33 additions and 54 deletions

View File

@ -5,9 +5,15 @@ test:
@echo Running tests... @echo Running tests...
go test -v $(TEST) go test -v $(TEST)
# Gateway-focused E2E tests assume gateway and nodes are already running
# Configure via env:
# GATEWAY_BASE_URL (default http://127.0.0.1:8080)
# GATEWAY_API_KEY (required for auth-protected routes)
.PHONY: test-e2e .PHONY: test-e2e
test-e2e: test-e2e:
@echo Running E2E tests... @echo "Running gateway E2E tests (HTTP/WS only)..."
@echo "Base URL: $${GATEWAY_BASE_URL:-http://127.0.0.1:8080}"
@test -n "$$GATEWAY_API_KEY" || (echo "GATEWAY_API_KEY must be set" && exit 1)
go test -v -tags e2e ./e2e go test -v -tags e2e ./e2e
# Network - Distributed P2P Database System # Network - Distributed P2P Database System
@ -37,11 +43,6 @@ clean:
rm -rf data/ rm -rf data/
@echo "Clean complete!" @echo "Clean complete!"
# Run tests
test:
@echo "Running tests..."
go test -v ./...
# Run bootstrap node (auto-selects identity and data dir) # Run bootstrap node (auto-selects identity and data dir)
run-node: run-node:
@echo "Starting bootstrap node with config..." @echo "Starting bootstrap node with config..."
@ -151,30 +152,6 @@ dev-setup: deps
@mkdir -p data/test-bootstrap data/test-node1 data/test-node2 @mkdir -p data/test-bootstrap data/test-node1 data/test-node2
@echo "Development setup complete!" @echo "Development setup complete!"
# Multi-node testing
test-multinode: build
@echo "🧪 Starting comprehensive multi-node test..."
@chmod +x scripts/test-multinode.sh
@./scripts/test-multinode.sh
test-peer-discovery: build
@echo "🔍 Testing peer discovery (requires running nodes)..."
@echo "Connected peers:"
@./bin/network-cli peers --timeout 10s
test-replication: build
@echo "🔄 Testing data replication (requires running nodes)..."
@./bin/network-cli storage put "replication:test:$$(date +%s)" "Test data - $$(date)"
@sleep 2
@echo "Retrieving replicated data:"
@./bin/network-cli storage list replication:test:
test-consensus: build
@echo "🗄️ Testing database consensus (requires running nodes)..."
@./bin/network-cli query "CREATE TABLE IF NOT EXISTS consensus_test (id INTEGER PRIMARY KEY, test_data TEXT, timestamp TEXT)"
@./bin/network-cli query "INSERT INTO consensus_test (test_data, timestamp) VALUES ('Makefile test', '$$(date)')"
@./bin/network-cli query "SELECT * FROM consensus_test ORDER BY id DESC LIMIT 5"
# Start development cluster (requires multiple terminals) # Start development cluster (requires multiple terminals)
dev-cluster: dev-cluster:
@echo "To start a development cluster, run these commands in separate terminals:" @echo "To start a development cluster, run these commands in separate terminals:"

View File

@ -3,11 +3,11 @@
package e2e package e2e
import ( import (
"context"
"crypto/rand" "crypto/rand"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
@ -111,10 +111,9 @@ func TestGateway_Storage_PutGetListExistsDelete(t *testing.T) {
if err != nil { t.Fatalf("get do: %v", err) } if err != nil { t.Fatalf("get do: %v", err) }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { t.Fatalf("get status: %d", resp.StatusCode) } if resp.StatusCode != http.StatusOK { t.Fatalf("get status: %d", resp.StatusCode) }
got := make([]byte, len(payload)) got, _ := io.ReadAll(resp.Body)
n, _ := resp.Body.Read(got) if string(got) != string(payload) {
if n == 0 || string(got[:n]) != string(payload[:n]) { t.Fatalf("payload mismatch: want %q got %q", string(payload), string(got))
t.Fatalf("payload mismatch: want %q got %q", string(payload), string(got[:n]))
} }
} }

View File

@ -9,6 +9,7 @@ import (
"strings" "strings"
"time" "time"
"git.debros.io/DeBros/network/pkg/client"
"git.debros.io/DeBros/network/pkg/logging" "git.debros.io/DeBros/network/pkg/logging"
"git.debros.io/DeBros/network/pkg/storage" "git.debros.io/DeBros/network/pkg/storage"
"go.uber.org/zap" "go.uber.org/zap"
@ -97,10 +98,11 @@ func (g *Gateway) authMiddleware(next http.Handler) http.Handler {
// Look up API key in DB and derive namespace // Look up API key in DB and derive namespace
db := g.client.Database() db := g.client.Database()
ctx := r.Context() // Use internal auth for DB validation (auth not established yet)
internalCtx := client.WithInternalAuth(r.Context())
// Join to namespaces to resolve name in one query // Join to namespaces to resolve name in one query
q := "SELECT namespaces.name FROM api_keys JOIN namespaces ON api_keys.namespace_id = namespaces.id WHERE api_keys.key = ? LIMIT 1" q := "SELECT namespaces.name FROM api_keys JOIN namespaces ON api_keys.namespace_id = namespaces.id WHERE api_keys.key = ? LIMIT 1"
res, err := db.Query(ctx, q, key) res, err := db.Query(internalCtx, q, key)
if err != nil || res == nil || res.Count == 0 || len(res.Rows) == 0 || len(res.Rows[0]) == 0 { if err != nil || res == nil || res.Count == 0 || len(res.Rows) == 0 || len(res.Rows[0]) == 0 {
w.Header().Set("WWW-Authenticate", "Bearer error=\"invalid_token\"") w.Header().Set("WWW-Authenticate", "Bearer error=\"invalid_token\"")
writeError(w, http.StatusUnauthorized, "invalid API key") writeError(w, http.StatusUnauthorized, "invalid API key")
@ -122,9 +124,9 @@ func (g *Gateway) authMiddleware(next http.Handler) http.Handler {
} }
// Attach auth metadata to context for downstream use // Attach auth metadata to context for downstream use
ctx = context.WithValue(ctx, ctxKeyAPIKey, key) reqCtx := context.WithValue(r.Context(), ctxKeyAPIKey, key)
ctx = storage.WithNamespace(ctx, ns) reqCtx = storage.WithNamespace(reqCtx, ns)
next.ServeHTTP(w, r.WithContext(ctx)) next.ServeHTTP(w, r.WithContext(reqCtx))
}) })
} }
@ -224,14 +226,15 @@ func (g *Gateway) authorizationMiddleware(next http.Handler) http.Handler {
return return
} }
// Check ownership in DB // Check ownership in DB using internal auth context
db := g.client.Database() db := g.client.Database()
internalCtx := client.WithInternalAuth(ctx)
// Ensure namespace exists and get id // Ensure namespace exists and get id
if _, err := db.Query(ctx, "INSERT OR IGNORE INTO namespaces(name) VALUES (?)", ns); err != nil { if _, err := db.Query(internalCtx, "INSERT OR IGNORE INTO namespaces(name) VALUES (?)", ns); err != nil {
writeError(w, http.StatusInternalServerError, err.Error()) writeError(w, http.StatusInternalServerError, err.Error())
return return
} }
nres, err := db.Query(ctx, "SELECT id FROM namespaces WHERE name = ? LIMIT 1", ns) nres, err := db.Query(internalCtx, "SELECT id FROM namespaces WHERE name = ? LIMIT 1", ns)
if err != nil || nres == nil || nres.Count == 0 || len(nres.Rows) == 0 || len(nres.Rows[0]) == 0 { if err != nil || nres == nil || nres.Count == 0 || len(nres.Rows) == 0 || len(nres.Rows[0]) == 0 {
writeError(w, http.StatusForbidden, "namespace not found") writeError(w, http.StatusForbidden, "namespace not found")
return return
@ -239,7 +242,7 @@ func (g *Gateway) authorizationMiddleware(next http.Handler) http.Handler {
nsID := nres.Rows[0][0] nsID := nres.Rows[0][0]
q := "SELECT 1 FROM namespace_ownership WHERE namespace_id = ? AND owner_type = ? AND owner_id = ? LIMIT 1" q := "SELECT 1 FROM namespace_ownership WHERE namespace_id = ? AND owner_type = ? AND owner_id = ? LIMIT 1"
res, err := db.Query(ctx, q, nsID, ownerType, ownerID) res, err := db.Query(internalCtx, q, nsID, ownerType, ownerID)
if err != nil || res == nil || res.Count == 0 { if err != nil || res == nil || res.Count == 0 {
writeError(w, http.StatusForbidden, "forbidden: not an owner of namespace") writeError(w, http.StatusForbidden, "forbidden: not an owner of namespace")
return return