mirror of
https://github.com/DeBrosOfficial/network.git
synced 2026-01-30 11:33:04 +00:00
334 lines
12 KiB
Go
334 lines
12 KiB
Go
//go:build e2e
|
|
|
|
package shared_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
"unicode"
|
|
|
|
e2e "github.com/DeBrosOfficial/network/e2e"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// =============================================================================
|
|
// STRICT AUTHENTICATION NEGATIVE TESTS
|
|
// These tests verify that authentication is properly enforced.
|
|
// Tests FAIL if unauthenticated/invalid requests are allowed through.
|
|
// =============================================================================
|
|
|
|
func TestAuth_MissingAPIKey(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request protected endpoint without auth headers
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
|
|
require.NoError(t, err, "FAIL: Could not create request")
|
|
|
|
client := e2e.NewHTTPClient(30 * time.Second)
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
defer resp.Body.Close()
|
|
|
|
// STRICT: Must reject requests without authentication
|
|
require.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden,
|
|
"FAIL: Protected endpoint allowed request without auth - expected 401/403, got %d", resp.StatusCode)
|
|
t.Logf(" ✓ Missing API key correctly rejected with status %d", resp.StatusCode)
|
|
}
|
|
|
|
func TestAuth_InvalidAPIKey(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request with invalid API key
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
|
|
require.NoError(t, err, "FAIL: Could not create request")
|
|
|
|
req.Header.Set("Authorization", "Bearer invalid-key-xyz-123456789")
|
|
|
|
client := e2e.NewHTTPClient(30 * time.Second)
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
defer resp.Body.Close()
|
|
|
|
// STRICT: Must reject invalid API keys
|
|
require.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden,
|
|
"FAIL: Invalid API key was accepted - expected 401/403, got %d", resp.StatusCode)
|
|
t.Logf(" ✓ Invalid API key correctly rejected with status %d", resp.StatusCode)
|
|
}
|
|
|
|
func TestAuth_CacheWithoutAuth(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request cache endpoint without auth
|
|
req := &e2e.HTTPRequest{
|
|
Method: http.MethodGet,
|
|
URL: e2e.GetGatewayURL() + "/v1/cache/health",
|
|
SkipAuth: true,
|
|
}
|
|
|
|
_, status, err := req.Do(ctx)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
|
|
// STRICT: Cache endpoint must require authentication
|
|
require.True(t, status == http.StatusUnauthorized || status == http.StatusForbidden,
|
|
"FAIL: Cache endpoint accessible without auth - expected 401/403, got %d", status)
|
|
t.Logf(" ✓ Cache endpoint correctly requires auth (status %d)", status)
|
|
}
|
|
|
|
func TestAuth_StorageWithoutAuth(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request storage endpoint without auth
|
|
req := &e2e.HTTPRequest{
|
|
Method: http.MethodGet,
|
|
URL: e2e.GetGatewayURL() + "/v1/storage/status/QmTest",
|
|
SkipAuth: true,
|
|
}
|
|
|
|
_, status, err := req.Do(ctx)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
|
|
// STRICT: Storage endpoint must require authentication
|
|
require.True(t, status == http.StatusUnauthorized || status == http.StatusForbidden,
|
|
"FAIL: Storage endpoint accessible without auth - expected 401/403, got %d", status)
|
|
t.Logf(" ✓ Storage endpoint correctly requires auth (status %d)", status)
|
|
}
|
|
|
|
func TestAuth_RQLiteWithoutAuth(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request rqlite endpoint without auth
|
|
req := &e2e.HTTPRequest{
|
|
Method: http.MethodGet,
|
|
URL: e2e.GetGatewayURL() + "/v1/rqlite/schema",
|
|
SkipAuth: true,
|
|
}
|
|
|
|
_, status, err := req.Do(ctx)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
|
|
// STRICT: RQLite endpoint must require authentication
|
|
require.True(t, status == http.StatusUnauthorized || status == http.StatusForbidden,
|
|
"FAIL: RQLite endpoint accessible without auth - expected 401/403, got %d", status)
|
|
t.Logf(" ✓ RQLite endpoint correctly requires auth (status %d)", status)
|
|
}
|
|
|
|
func TestAuth_MalformedBearerToken(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request with malformed bearer token (missing "Bearer " prefix)
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
|
|
require.NoError(t, err, "FAIL: Could not create request")
|
|
|
|
req.Header.Set("Authorization", "invalid-token-format-no-bearer")
|
|
|
|
client := e2e.NewHTTPClient(30 * time.Second)
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
defer resp.Body.Close()
|
|
|
|
// STRICT: Must reject malformed authorization headers
|
|
require.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden,
|
|
"FAIL: Malformed auth header accepted - expected 401/403, got %d", resp.StatusCode)
|
|
t.Logf(" ✓ Malformed bearer token correctly rejected (status %d)", resp.StatusCode)
|
|
}
|
|
|
|
func TestAuth_ExpiredJWT(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Test with a clearly invalid JWT structure
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
|
|
require.NoError(t, err, "FAIL: Could not create request")
|
|
|
|
req.Header.Set("Authorization", "Bearer expired.jwt.token.invalid")
|
|
|
|
client := e2e.NewHTTPClient(30 * time.Second)
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
defer resp.Body.Close()
|
|
|
|
// STRICT: Must reject invalid/expired JWT tokens
|
|
require.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden,
|
|
"FAIL: Invalid JWT accepted - expected 401/403, got %d", resp.StatusCode)
|
|
t.Logf(" ✓ Invalid JWT correctly rejected (status %d)", resp.StatusCode)
|
|
}
|
|
|
|
func TestAuth_EmptyBearerToken(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request with empty bearer token
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
|
|
require.NoError(t, err, "FAIL: Could not create request")
|
|
|
|
req.Header.Set("Authorization", "Bearer ")
|
|
|
|
client := e2e.NewHTTPClient(30 * time.Second)
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
defer resp.Body.Close()
|
|
|
|
// STRICT: Must reject empty bearer tokens
|
|
require.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden,
|
|
"FAIL: Empty bearer token accepted - expected 401/403, got %d", resp.StatusCode)
|
|
t.Logf(" ✓ Empty bearer token correctly rejected (status %d)", resp.StatusCode)
|
|
}
|
|
|
|
func TestAuth_DuplicateAuthHeaders(t *testing.T) {
|
|
if e2e.GetAPIKey() == "" {
|
|
t.Skip("No API key configured")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request with both valid API key in Authorization header
|
|
req := &e2e.HTTPRequest{
|
|
Method: http.MethodGet,
|
|
URL: e2e.GetGatewayURL() + "/v1/cache/health",
|
|
Headers: map[string]string{
|
|
"Authorization": "Bearer " + e2e.GetAPIKey(),
|
|
"X-API-Key": e2e.GetAPIKey(),
|
|
},
|
|
}
|
|
|
|
_, status, err := req.Do(ctx)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
|
|
// Should succeed since we have a valid API key
|
|
require.Equal(t, http.StatusOK, status,
|
|
"FAIL: Valid API key rejected when multiple auth headers present - got %d", status)
|
|
t.Logf(" ✓ Duplicate auth headers with valid key succeeds (status %d)", status)
|
|
}
|
|
|
|
func TestAuth_CaseSensitiveAPIKey(t *testing.T) {
|
|
apiKey := e2e.GetAPIKey()
|
|
if apiKey == "" {
|
|
t.Skip("No API key configured")
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Create incorrectly cased API key
|
|
incorrectKey := ""
|
|
for i, ch := range apiKey {
|
|
if i%2 == 0 && unicode.IsLetter(ch) {
|
|
if unicode.IsLower(ch) {
|
|
incorrectKey += string(unicode.ToUpper(ch))
|
|
} else {
|
|
incorrectKey += string(unicode.ToLower(ch))
|
|
}
|
|
} else {
|
|
incorrectKey += string(ch)
|
|
}
|
|
}
|
|
|
|
// Skip if the key didn't change (no letters)
|
|
if incorrectKey == apiKey {
|
|
t.Skip("API key has no letters to change case")
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/cache/health", nil)
|
|
require.NoError(t, err, "FAIL: Could not create request")
|
|
|
|
req.Header.Set("Authorization", "Bearer "+incorrectKey)
|
|
|
|
client := e2e.NewHTTPClient(30 * time.Second)
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
defer resp.Body.Close()
|
|
|
|
// STRICT: API keys MUST be case-sensitive
|
|
require.True(t, resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden,
|
|
"FAIL: API key check is not case-sensitive - modified key accepted with status %d", resp.StatusCode)
|
|
t.Logf(" ✓ Case-modified API key correctly rejected (status %d)", resp.StatusCode)
|
|
}
|
|
|
|
func TestAuth_HealthEndpointNoAuth(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Health endpoint at /v1/health should NOT require auth
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/health", nil)
|
|
require.NoError(t, err, "FAIL: Could not create request")
|
|
|
|
client := e2e.NewHTTPClient(30 * time.Second)
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
defer resp.Body.Close()
|
|
|
|
// Health endpoint should be publicly accessible
|
|
require.Equal(t, http.StatusOK, resp.StatusCode,
|
|
"FAIL: Health endpoint should not require auth - expected 200, got %d", resp.StatusCode)
|
|
t.Logf(" ✓ Health endpoint correctly accessible without auth")
|
|
}
|
|
|
|
func TestAuth_StatusEndpointNoAuth(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Status endpoint at /v1/status should NOT require auth
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, e2e.GetGatewayURL()+"/v1/status", nil)
|
|
require.NoError(t, err, "FAIL: Could not create request")
|
|
|
|
client := e2e.NewHTTPClient(30 * time.Second)
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
defer resp.Body.Close()
|
|
|
|
// Status endpoint should be publicly accessible
|
|
require.Equal(t, http.StatusOK, resp.StatusCode,
|
|
"FAIL: Status endpoint should not require auth - expected 200, got %d", resp.StatusCode)
|
|
t.Logf(" ✓ Status endpoint correctly accessible without auth")
|
|
}
|
|
|
|
func TestAuth_DeploymentsWithoutAuth(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request deployments endpoint without auth
|
|
req := &e2e.HTTPRequest{
|
|
Method: http.MethodGet,
|
|
URL: e2e.GetGatewayURL() + "/v1/deployments/list",
|
|
SkipAuth: true,
|
|
}
|
|
|
|
_, status, err := req.Do(ctx)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
|
|
// STRICT: Deployments endpoint must require authentication
|
|
require.True(t, status == http.StatusUnauthorized || status == http.StatusForbidden,
|
|
"FAIL: Deployments endpoint accessible without auth - expected 401/403, got %d", status)
|
|
t.Logf(" ✓ Deployments endpoint correctly requires auth (status %d)", status)
|
|
}
|
|
|
|
func TestAuth_SQLiteWithoutAuth(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// Request SQLite endpoint without auth
|
|
req := &e2e.HTTPRequest{
|
|
Method: http.MethodGet,
|
|
URL: e2e.GetGatewayURL() + "/v1/db/sqlite/list",
|
|
SkipAuth: true,
|
|
}
|
|
|
|
_, status, err := req.Do(ctx)
|
|
require.NoError(t, err, "FAIL: Request failed")
|
|
|
|
// STRICT: SQLite endpoint must require authentication
|
|
require.True(t, status == http.StatusUnauthorized || status == http.StatusForbidden,
|
|
"FAIL: SQLite endpoint accessible without auth - expected 401/403, got %d", status)
|
|
t.Logf(" ✓ SQLite endpoint correctly requires auth (status %d)", status)
|
|
}
|