in progress
This commit is contained in:
@@ -0,0 +1,392 @@
|
||||
//go:build ignore && manual_auth_tests
|
||||
// +build ignore,manual_auth_tests
|
||||
|
||||
// This file is excluded from all builds.
|
||||
// It contains integration tests that require external dependencies which
|
||||
// shouldn't be included in the main project's go.mod.
|
||||
//
|
||||
// The "ignore" build tag ensures this file won't be considered by Go tools,
|
||||
// including go mod tidy.
|
||||
|
||||
package cognitotest
|
||||
|
||||
// Standard imports that don't need special handling
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"queryorchestration/internal/cognitoauth"
|
||||
"queryorchestration/internal/serviceconfig/auth"
|
||||
|
||||
"github.com/chromedp/cdproto/network"
|
||||
"github.com/chromedp/chromedp"
|
||||
)
|
||||
|
||||
// setupEnvironment ensures all required environment variables are set
|
||||
func setupEnvironment(t *testing.T) {
|
||||
// Define all required environment variables with test values
|
||||
envVars := map[string]string{
|
||||
"USER": os.Getenv("USER"),
|
||||
"PASSWORD": os.Getenv("PASSWORD"),
|
||||
"COGNITO_DOMAIN": os.Getenv("COGNITO_DOMAIN"),
|
||||
"AWS_REGION": os.Getenv("AWS_REGION"),
|
||||
"COGNITO_CLIENT_SECRET": os.Getenv("COGNITO_CLIENT_SECRET"),
|
||||
"COGNITO_USER_POOL_ID": os.Getenv("COGNITO_USER_POOL_ID"),
|
||||
"COGNITO_CLIENT_ID": os.Getenv("COGNITO_CLIENT_ID"),
|
||||
"DEBUG": "true",
|
||||
"HEADLESS": os.Getenv("HEADLESS"),
|
||||
}
|
||||
|
||||
// Verify all required variables are set
|
||||
for key, value := range envVars {
|
||||
if value == "" {
|
||||
// HEADLESS is optional with a default of "true"
|
||||
if key == "HEADLESS" {
|
||||
os.Setenv(key, "true")
|
||||
continue
|
||||
}
|
||||
t.Fatalf("Required environment variable %s is not set", key)
|
||||
}
|
||||
// Set the environment variable for the test
|
||||
os.Setenv(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
// setupTestServer creates and configures an Echo server for testing
|
||||
func setupTestServer(t *testing.T) (*http.Server, *auth.CognitoConfig) {
|
||||
// Create a new Echo instance
|
||||
e := echo.New()
|
||||
|
||||
// Add basic middleware
|
||||
e.Use(middleware.Logger())
|
||||
e.Use(middleware.Recover())
|
||||
|
||||
// Initialize logger
|
||||
logHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug,
|
||||
})
|
||||
logger := slog.New(logHandler)
|
||||
|
||||
// Create auth config with correct base URL that matches Cognito settings
|
||||
baseURL := "http://localhost:8080"
|
||||
config := auth.InitializeConfig(baseURL, logger)
|
||||
|
||||
// Set route permissions based on test requirements
|
||||
routePermissions := map[string][]string{
|
||||
"/query": {"exporters", "uploaders", "querybuilders"},
|
||||
"/norights": {"exporters"}, // Test user should not have this permission
|
||||
}
|
||||
config.SetAuthRoutePermissions(routePermissions)
|
||||
|
||||
// Register Cognito auth routes and middleware
|
||||
cognitoauth.RegisterRoutes(e, config)
|
||||
|
||||
// Add test endpoints
|
||||
e.GET("/home", func(c echo.Context) error {
|
||||
// Check if user is authenticated
|
||||
userInfo, authenticated := cognitoauth.GetUserInfo(c)
|
||||
|
||||
response := map[string]interface{}{
|
||||
"authenticated": authenticated,
|
||||
}
|
||||
|
||||
if authenticated {
|
||||
response["username"] = userInfo.Username
|
||||
response["email"] = userInfo.Email
|
||||
response["groups"] = userInfo.Groups
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, response)
|
||||
})
|
||||
|
||||
e.GET("/query", func(c echo.Context) error {
|
||||
return c.JSON(http.StatusOK, map[string]string{
|
||||
"status": "success",
|
||||
"message": "You have access to the query endpoint",
|
||||
})
|
||||
})
|
||||
|
||||
e.GET("/norights", func(c echo.Context) error {
|
||||
return c.JSON(http.StatusOK, map[string]string{
|
||||
"status": "success",
|
||||
"message": "You should not be able to access this endpoint",
|
||||
})
|
||||
})
|
||||
|
||||
// Create server with fixed address to match Cognito config
|
||||
server := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: e,
|
||||
ReadHeaderTimeout: 30 * time.Second, // <-- Add this line
|
||||
}
|
||||
|
||||
// Log the redirect URL for debugging
|
||||
logger.Info("Using redirect URI", "uri", config.GetAuthRedirectURI())
|
||||
|
||||
return server, config
|
||||
}
|
||||
|
||||
// TestCognitoAuthFlow runs the complete end-to-end test
|
||||
func TestCognitoAuthFlow(t *testing.T) {
|
||||
if os.Getenv("ENABLE_AUTH_TESTS") != "true" {
|
||||
t.Skip("Skipping auth end to end tests")
|
||||
}
|
||||
|
||||
// Skip if running in CI environment or short test mode
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
// Setup test environment
|
||||
setupEnvironment(t)
|
||||
|
||||
// Get credentials from environment
|
||||
username := os.Getenv("USER")
|
||||
password := os.Getenv("PASSWORD")
|
||||
|
||||
// Setup test server
|
||||
server, config := setupTestServer(t)
|
||||
|
||||
// Start the server in a goroutine
|
||||
go func() {
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
t.Logf("Server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Ensure server shuts down at the end of the test
|
||||
defer func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
t.Logf("Server shutdown error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Give the server time to start
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
// Log important URLs for debugging
|
||||
baseURL := "http://localhost:8080"
|
||||
t.Logf("Server URL: %s", baseURL)
|
||||
t.Logf("Login URL: %s%s", baseURL, config.GetAuthLoginPath())
|
||||
t.Logf("Callback URL: %s", config.GetAuthRedirectURI())
|
||||
|
||||
// Get headless mode from environment variable
|
||||
headless := true
|
||||
if os.Getenv("HEADLESS") == "false" {
|
||||
headless = false
|
||||
}
|
||||
|
||||
// Setup Chrome options
|
||||
opts := append(chromedp.DefaultExecAllocatorOptions[:],
|
||||
// Headless mode controlled by HEADLESS environment variable
|
||||
chromedp.Flag("headless", headless),
|
||||
chromedp.Flag("disable-gpu", true),
|
||||
chromedp.Flag("no-sandbox", true),
|
||||
chromedp.Flag("disable-dev-shm-usage", true),
|
||||
// Block Prometheus metrics requests that are interfering
|
||||
chromedp.Flag("host-blocking-patterns", "*prometheus*"),
|
||||
// Disable browser cache
|
||||
chromedp.Flag("disable-cache", true),
|
||||
)
|
||||
|
||||
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
|
||||
defer cancel()
|
||||
|
||||
// Create Chrome context with longer timeout for authentication flow
|
||||
ctx, cancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))
|
||||
defer cancel()
|
||||
ctx, cancel = context.WithTimeout(ctx, 5*time.Minute) // Extended timeout
|
||||
defer cancel()
|
||||
|
||||
// Enable request interception for debugging
|
||||
if err := chromedp.Run(ctx, network.Enable()); err != nil {
|
||||
t.Fatalf("Failed to enable network: %v", err)
|
||||
}
|
||||
|
||||
// Listen for events to track redirects
|
||||
chromedp.ListenTarget(ctx, func(ev interface{}) {
|
||||
switch e := ev.(type) {
|
||||
case *network.EventRequestWillBeSent:
|
||||
t.Logf("Request: %s %s", e.Request.Method, e.Request.URL)
|
||||
case *network.EventResponseReceived:
|
||||
t.Logf("Response: %s %s (status: %d)", e.Response.URL, e.Response.MimeType, e.Response.Status)
|
||||
}
|
||||
})
|
||||
|
||||
// Store cookies to verify JWT token later
|
||||
var cookies []*network.Cookie
|
||||
|
||||
// 1. Navigate to login page, which will redirect to Cognito
|
||||
loginURL := fmt.Sprintf("%s%s", baseURL, config.GetAuthLoginPath())
|
||||
t.Run("Navigate to login page", func(t *testing.T) {
|
||||
err := chromedp.Run(ctx,
|
||||
chromedp.Navigate(loginURL),
|
||||
chromedp.WaitVisible(`input[name="username"]`, chromedp.ByQuery),
|
||||
)
|
||||
assert.NoError(t, err, "Failed to navigate to login page")
|
||||
|
||||
// Log current URL to verify redirect
|
||||
var currentURL string
|
||||
err = chromedp.Run(ctx, chromedp.Location(¤tURL))
|
||||
assert.NoError(t, err)
|
||||
t.Logf("Current URL after navigation: %s", currentURL)
|
||||
})
|
||||
|
||||
// 2. Submit login credentials
|
||||
t.Run("Submit login credentials", func(t *testing.T) {
|
||||
// Wait for the username field and enter the username
|
||||
err := chromedp.Run(ctx,
|
||||
chromedp.WaitVisible(`input[name="username"]`, chromedp.ByQuery),
|
||||
chromedp.Clear(`input[name="username"]`, chromedp.ByQuery),
|
||||
chromedp.SendKeys(`input[name="username"]`, username, chromedp.ByQuery),
|
||||
// Click the "Continue" button for username step
|
||||
chromedp.Click(`button[type="submit"], button.amplify-button--primary, input[type="Submit"]`, chromedp.ByQuery),
|
||||
)
|
||||
assert.NoError(t, err, "Failed to submit username")
|
||||
|
||||
// Wait for password field to appear and enter password
|
||||
err = chromedp.Run(ctx,
|
||||
chromedp.WaitVisible(`input[name="password"]`, chromedp.ByQuery),
|
||||
chromedp.Clear(`input[name="password"]`, chromedp.ByQuery),
|
||||
chromedp.SendKeys(`input[name="password"]`, password, chromedp.ByQuery),
|
||||
// Click the sign-in button for password step
|
||||
chromedp.Sleep(1*time.Second), // Give a moment for button to be fully active
|
||||
chromedp.Click(`button[type="submit"], button.amplify-button--primary, input[type="Submit"]`, chromedp.ByQuery),
|
||||
)
|
||||
assert.NoError(t, err, "Failed to submit password")
|
||||
|
||||
// Log that we submitted credentials
|
||||
t.Logf("Submitted login credentials for user: %s", username)
|
||||
})
|
||||
|
||||
// 3. Wait for redirection to home page after successful login
|
||||
var homePageContent string
|
||||
t.Run("Verify redirection to home page", func(t *testing.T) {
|
||||
// Wait for the redirect to complete - we need to handle multiple redirects:
|
||||
// 1. From Cognito to our callback endpoint with the authorization code
|
||||
// 2. From callback to home page
|
||||
err := chromedp.Run(ctx, chromedp.ActionFunc(func(ctx context.Context) error {
|
||||
// Wait for up to 45 seconds
|
||||
startTime := time.Now()
|
||||
timeout := 45 * time.Second
|
||||
|
||||
for {
|
||||
if time.Since(startTime) > timeout {
|
||||
return fmt.Errorf("timed out waiting for redirect")
|
||||
}
|
||||
|
||||
// Check current URL
|
||||
var currentURL string
|
||||
if err := chromedp.Location(¤tURL).Do(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.Logf("Current URL during redirect wait: %s", currentURL)
|
||||
|
||||
// If we're at home page, we're done
|
||||
if strings.Contains(currentURL, "/home") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we're at the callback page, wait a bit for the next redirect
|
||||
if strings.Contains(currentURL, "/login-callback") {
|
||||
t.Log("At callback page, waiting for processing...")
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// Still waiting for redirect
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}))
|
||||
assert.NoError(t, err, "Failed to wait for redirect after login")
|
||||
|
||||
// Get the final page content
|
||||
err = chromedp.Run(ctx,
|
||||
chromedp.Sleep(2*time.Second), // Give time for page to fully load
|
||||
chromedp.OuterHTML("body", &homePageContent),
|
||||
chromedp.ActionFunc(func(ctx context.Context) error {
|
||||
cookies, err = network.GetCookies().Do(ctx)
|
||||
return err
|
||||
}),
|
||||
)
|
||||
assert.NoError(t, err, "Failed to get home page content")
|
||||
|
||||
// Log cookie names for debugging
|
||||
cookieNames := make([]string, 0, len(cookies))
|
||||
for _, cookie := range cookies {
|
||||
cookieNames = append(cookieNames, cookie.Name)
|
||||
}
|
||||
t.Logf("Found cookies: %v", cookieNames)
|
||||
|
||||
// Verify we have the home page content with authentication info
|
||||
assert.Contains(t, homePageContent, "authenticated", "Home page should show authenticated status")
|
||||
})
|
||||
|
||||
// 4. Verify JWT token in cookies
|
||||
var authToken string
|
||||
t.Run("Verify JWT token cookie", func(t *testing.T) {
|
||||
for _, cookie := range cookies {
|
||||
if cookie.Name == "auth_token" {
|
||||
authToken = cookie.Value
|
||||
break
|
||||
}
|
||||
}
|
||||
assert.NotEmpty(t, authToken, "JWT token cookie not found")
|
||||
t.Logf("Found auth_token cookie")
|
||||
})
|
||||
|
||||
// 5. Try accessing protected endpoints
|
||||
t.Run("Access endpoints with correct token", func(t *testing.T) {
|
||||
// First test /query endpoint (should succeed)
|
||||
queryURL := fmt.Sprintf("%s/query", baseURL)
|
||||
var queryResponseBody string
|
||||
|
||||
err := chromedp.Run(ctx,
|
||||
chromedp.Navigate(queryURL),
|
||||
chromedp.Sleep(3*time.Second), // Give time for page to load
|
||||
chromedp.OuterHTML("body", &queryResponseBody),
|
||||
)
|
||||
assert.NoError(t, err, "Failed to access authorized endpoint")
|
||||
t.Logf("Query endpoint response: %s", queryResponseBody)
|
||||
assert.Contains(t, queryResponseBody, "success", "Should have access to /query endpoint")
|
||||
|
||||
// Then test /norights endpoint (should fail)
|
||||
noRightsURL := fmt.Sprintf("%s/norights", baseURL)
|
||||
var noRightsResponseBody string
|
||||
|
||||
err = chromedp.Run(ctx,
|
||||
chromedp.Navigate(noRightsURL),
|
||||
chromedp.Sleep(3*time.Second), // Give time for page to load
|
||||
chromedp.OuterHTML("body", &noRightsResponseBody),
|
||||
)
|
||||
assert.NoError(t, err, "Failed to navigate to unauthorized endpoint")
|
||||
t.Logf("NoRights endpoint response: %s", noRightsResponseBody)
|
||||
assert.Contains(t, noRightsResponseBody, "Insufficient permissions",
|
||||
"Should NOT have access to /norights endpoint")
|
||||
|
||||
// Test successful
|
||||
t.Log("✅ All tests passed! Authentication flow verified successfully.")
|
||||
})
|
||||
}
|
||||
|
||||
// Helper function to decode JSON response bodies for verification
|
||||
//func decodeJSONResponse(responseBody string) (map[string]interface{}, error) {
|
||||
// var response map[string]interface{}
|
||||
// decoder := json.NewDecoder(strings.NewReader(responseBody))
|
||||
// err := decoder.Decode(&response)
|
||||
// return response, err
|
||||
//}
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
export USER=betot75403@isorax.com
|
||||
export PASSWORD=qqQQ11!!
|
||||
export COGNITO_DOMAIN=https://us-east-21y6po8rr8.auth.us-east-2.amazoncognito.com
|
||||
export AWS_REGION=us-east-2
|
||||
export COGNITO_CLIENT_SECRET=aaknqeq9ajr07qjch4tkq38ulghvn2i8v7tn3d2fcv44uevfemf
|
||||
export COGNITO_USER_POOL_ID=us-east-2_1y6po8rR8
|
||||
export COGNITO_CLIENT_ID=552cqkf3640t39ncehkmgpce31
|
||||
export DEBUG=true
|
||||
export HEADLESS=false
|
||||
export ENABLE_AUTH_TESTS=true
|
||||
|
||||
# run test without flags
|
||||
#go test -v -timeout 3m
|
||||
|
||||
# Run the test with the manual flag
|
||||
go test -tags manual_auth_tests -v -timeout 3m
|
||||
|
||||
|
||||
Reference in New Issue
Block a user