Files
query-orchestration/cmd/auth_related/cognito.auth.harness/main.go
T
Jay Brown 6dccf494f8 Merged in feature/permit-policy-cleanup (pull request #210)
cleanup permit policies and correct documentation

* docs

* policy cleanup

* refactor

* Merge remote-tracking branch 'origin/feature/permit-policy-cleanup' into feature/permit-policy-cleanup

* docs and edits
2026-02-19 20:22:59 +00:00

289 lines
7.7 KiB
Go

package main
import (
"context"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
"time"
"queryorchestration/internal/serviceconfig/auth"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/lestrrat-go/jwx/v2/jwt"
"queryorchestration/internal/cognitoauth" // Replace with your actual package import path
)
var endpointsList = []string{
// Auth Service Endpoints
"/login",
"/login-callback",
"/logout",
"/home",
// Query API Endpoints
"/query",
// Document Service Endpoints
"/settings",
// Export Service Endpoints
"/export",
}
// HomeHandlerFunc returns the HomeHandler as an echo.HandlerFunc
func HomeHandlerFunc() echo.HandlerFunc {
return HomeHandler
}
// homeHandler renders a simple HTML page with links to all endpoints
// and displays authentication status based on JWT token
func HomeHandler(c echo.Context) error {
var linksHTML string
baseURL := "http://localhost:8080"
// Create list items for each endpoint
for _, endpoint := range endpointsList {
// Skip endpoints with path parameters for direct linking
if strings.Contains(endpoint, ":") {
displayPath := strings.Replace(endpoint, ":id", "{id}", -1)
linksHTML += fmt.Sprintf("<li>%s (requires parameter)</li>\n", displayPath)
} else {
linksHTML += fmt.Sprintf("<li><a href=\"%s%s\">%s</a></li>\n", baseURL, endpoint, endpoint)
}
}
// Check if user is authenticated by looking for token in cookie
tokenCookie, err := c.Cookie("auth_token")
isAuthenticated := (err == nil && tokenCookie.Value != "")
// Variables for user info
username := ""
email := ""
var userGroups []string
// If authenticated, try to decode the JWT token to get user info
if isAuthenticated {
// Parse the JWT token without verification (just to extract info for display)
// In a production application, you would want to verify the token here as well
token, _ := jwt.Parse([]byte(tokenCookie.Value), jwt.WithVerify(false))
if token != nil {
claims, _ := token.AsMap(context.Background())
username, _ = claims["cognito:username"].(string)
email, _ = claims["email"].(string)
// Try to extract groups
if rawGroups, ok := claims["cognito:groups"]; ok {
if groups, ok := rawGroups.([]interface{}); ok {
userGroups = make([]string, len(groups))
for i, g := range groups {
userGroups[i] = fmt.Sprint(g)
}
}
}
}
}
// Create authentication status section
var authStatusHTML string
if isAuthenticated {
authStatusHTML = fmt.Sprintf(`
<div class="auth-status authenticated">
<h2>Authentication Status: Authenticated</h2>
<p><strong>Username:</strong> %s</p>
<p><strong>Email:</strong> %s</p>
<p><strong>Groups:</strong> %s</p>
<p><a href="/logout" class="logout-btn">Logout</a></p>
</div>
`, username, email, strings.Join(userGroups, ", "))
} else {
authStatusHTML = `
<div class="auth-status unauthenticated">
<h2>Authentication Status: Not Authenticated</h2>
<p>You are not currently logged in.</p>
<p><a href="/login" class="login-btn">Login</a></p>
</div>
`
}
// Create the complete HTML page
html := fmt.Sprintf(`
<!DOCTYPE html>
<html>
<head>
<title>Doczy Home</title>
<style>
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 20px;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
h1, h2 {
color: #333;
border-bottom: 1px solid #ddd;
padding-bottom: 10px;
}
ul {
margin-top: 20px;
}
li {
margin-bottom: 8px;
}
a {
color: #0066cc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.note {
background-color: #f8f9fa;
border-left: 4px solid #5bc0de;
padding: 10px 15px;
margin-top: 20px;
font-size: 0.9em;
}
.auth-status {
margin: 20px 0;
padding: 15px;
border-radius: 5px;
}
.authenticated {
background-color: #dff0d8;
border: 1px solid #d6e9c6;
}
.unauthenticated {
background-color: #f2dede;
border: 1px solid #ebccd1;
}
.login-btn, .logout-btn {
display: inline-block;
padding: 8px 16px;
background-color: #0066cc;
color: white;
border-radius: 4px;
text-decoration: none;
margin-top: 10px;
}
.logout-btn {
background-color: #d9534f;
}
</style>
</head>
<body>
<h1>Doczy Home</h1>
%s
<h2>Available Endpoints</h2>
<ul>
%s
</ul>
<div class="note">
<p><strong>Note:</strong> This is a debugging page. Some endpoints require authentication or specific permissions.</p>
</div>
</body>
</html>
`, authStatusHTML, linksHTML)
return c.HTML(http.StatusOK, html)
}
func main() {
// Create a new Echo instance
e := echo.New()
// Add basic middleware
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// Initialize logger with appropriate level based on DEBUG env var
var logLevel slog.Level
if os.Getenv("DEBUG") == "true" {
logLevel = slog.LevelDebug
} else {
logLevel = slog.LevelInfo
}
logHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: logLevel,
})
logger := slog.New(logHandler)
// Create auth config from environment variables
config := auth.InitializeConfig("http://localhost:8080", logger)
// Set route permissions
routePermissions := map[string][]string{
"/query": {"exporters", "uploaders", "querybuilders"},
"/users/:id": {"exporters", "querybuilders"},
"/export": {"exporters"},
"/reports/sales": {"exporters", "uploaders", "querybuilders"},
}
config.SetAuthRoutePermissions(routePermissions)
// Register authentication routes
cognitoauth.RegisterRoutes(e, config)
// Register your application routes
// These will be protected based on the permissions set above
e.GET("/users", func(c echo.Context) error {
return c.String(http.StatusOK, "Users endpoint (OK)")
})
e.GET("/users/:id", func(c echo.Context) error {
id := c.Param("id")
return c.String(http.StatusOK, "User details endpoint for ID: "+id)
})
e.GET("/query", func(c echo.Context) error {
return c.String(http.StatusOK, "query endpoint (OK)")
})
e.GET("/reports/sales", func(c echo.Context) error {
return c.String(http.StatusOK, "Sales reports endpoint (OK)")
})
e.GET("/settings", func(c echo.Context) error {
return c.String(http.StatusOK, "document endpoint (OK)")
})
// add entry for /home to use the HomeHandlerFunc
e.GET("/home", HomeHandlerFunc())
e.PUT("/api/inventory/update", func(c echo.Context) error {
return c.String(http.StatusOK, "Inventory update endpoint (OK)")
})
// Legacy group-based middleware removed (replaced with Permit.io)
// Alternative approach: Use the RequireGroups middleware for specific routes
// This can be used instead of or in addition to the global permissions map
// adminGroup := e.Group("/admin")
// adminGroup.Use(cognitoauth.RequireGroups("admin")) // Only admin group can access this route
// adminGroup.GET("/dashboard", func(c echo.Context) error {
// return c.String(http.StatusOK, "Admin Dashboard (OK)")
// })
// Configure server
server := &http.Server{
Addr: ":8080",
ReadHeaderTimeout: 3 * time.Second,
ReadTimeout: 20 * time.Second,
WriteTimeout: 20 * time.Second,
IdleTimeout: 120 * time.Second,
}
// Start server
logger.Info("Server starting on :8080")
e.Logger.Fatal(e.StartServer(server))
}