config integration
begin cognito config integration
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
|
||||
"queryorchestration/internal/cognitoauth" // Replace with your actual package import path
|
||||
)
|
||||
|
||||
|
||||
@@ -34,7 +34,6 @@ import (
|
||||
"queryorchestration/internal/server/api"
|
||||
"queryorchestration/internal/serviceconfig/queue/clientsync"
|
||||
"queryorchestration/internal/serviceconfig/queue/queryversionsync"
|
||||
"queryorchestration/internal/serviceconfig/rbac"
|
||||
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
_ "github.com/lib/pq"
|
||||
@@ -106,12 +105,13 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// note initialize cognito config here
|
||||
// Initialize the rbac config here since only the API service needs it
|
||||
errorGettingAuthProvider := rbac.InitializeAuthProvider(&cfg.AuthConfig)
|
||||
if errorGettingAuthProvider != nil {
|
||||
slog.Error(errorGettingAuthProvider.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
//errorGettingAuthProvider := rbac.InitializeAuthProvider(&cfg.AuthConfig)
|
||||
//if errorGettingAuthProvider != nil {
|
||||
// slog.Error(errorGettingAuthProvider.Error())
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
server, err := api.New(ctx, cfg)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// RegisterRoutes registers all authentication-related routes to the Echo engine
|
||||
func RegisterRoutes(e *echo.Echo, config *Config) {
|
||||
func RegisterRoutes(e *echo.Echo, config *CognitoConfig) {
|
||||
// Register the login route - the middleware will handle this
|
||||
e.GET(config.LoginPath, func(c echo.Context) error {
|
||||
// This is handled by the middleware
|
||||
@@ -53,7 +53,7 @@ func RegisterRoutes(e *echo.Echo, config *Config) {
|
||||
}
|
||||
|
||||
// HomeHandler implements a simple home page that shows auth status and available endpoints
|
||||
func HomeHandler(c echo.Context, config *Config) error {
|
||||
func HomeHandler(c echo.Context, config *CognitoConfig) error {
|
||||
// Create a list of all registered routes
|
||||
var endpoints []string
|
||||
|
||||
|
||||
@@ -8,27 +8,30 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Config holds the configuration for AWS Cognito
|
||||
// CognitoConfig holds the configuration for AWS Cognito
|
||||
// Contains all necessary parameters to interact with Cognito endpoints
|
||||
type Config struct {
|
||||
ClientID string // OAuth2 client ID registered with Cognito
|
||||
type CognitoConfig struct {
|
||||
RedirectURI string // URL where Cognito redirects after authentication
|
||||
TokenURL string // Cognito endpoint for token operations
|
||||
JwksURL string // URL for JSON Web Key Set (for token verification)
|
||||
UserPoolID string // Cognito User Pool ID
|
||||
AuthURL string // Cognito authorization endpoint for initiating login
|
||||
ClientSecret string // Client secret for authenticated clients
|
||||
Region string // AWS region where the Cognito User Pool is located
|
||||
LoginPath string // Path for initiating login
|
||||
CallbackPath string // Path for OAuth callback
|
||||
HomePath string // Path for home page
|
||||
LogoutPath string // Path for logout
|
||||
Logger *slog.Logger // Logger instance
|
||||
RoutePermissions map[string][]string // Map of routes to required permissions
|
||||
|
||||
// annotated vars
|
||||
Region string `env:"COGNITO_REGION" envDefault:"us-east-2"`
|
||||
UserPoolID string `env:"COGNITO_USER_POOL_ID" required:"true"`
|
||||
ClientSecret string `env:"COGNITO_CLIENT_SECRET" required:"true"`
|
||||
Domain string `env:"COGNITO_DOMAIN" required:"true"`
|
||||
ClientID string `env:"COGNITO_CLIENT_ID" required:"true"`
|
||||
}
|
||||
|
||||
// NewConfigFromEnv creates a new Config from environment variables
|
||||
func NewConfigFromEnv(baseURL string, logger *slog.Logger) *Config {
|
||||
// NewConfigFromEnv creates a new CognitoConfig from environment variables
|
||||
func NewConfigFromEnv(baseURL string, logger *slog.Logger) *CognitoConfig {
|
||||
// Set default logger if not provided
|
||||
if logger == nil {
|
||||
var logLevel slog.Level
|
||||
@@ -94,7 +97,7 @@ func NewConfigFromEnv(baseURL string, logger *slog.Logger) *Config {
|
||||
// Sanitize base URL (remove trailing slash)
|
||||
baseURL = strings.TrimSuffix(baseURL, "/")
|
||||
|
||||
config := &Config{
|
||||
config := &CognitoConfig{
|
||||
ClientID: os.Getenv("COGNITO_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("COGNITO_CLIENT_SECRET"),
|
||||
RedirectURI: baseURL + callbackPath,
|
||||
@@ -115,8 +118,8 @@ func NewConfigFromEnv(baseURL string, logger *slog.Logger) *Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (c *Config) PrettyPrint() {
|
||||
fmt.Printf("Config:\n")
|
||||
func (c *CognitoConfig) PrettyPrint() {
|
||||
fmt.Printf("CognitoConfig:\n")
|
||||
fmt.Printf(" ClientID: %s\n", c.ClientID)
|
||||
fmt.Printf(" ClientSecret: %s\n", c.ClientSecret)
|
||||
fmt.Printf(" RedirectURI: %s\n", c.RedirectURI)
|
||||
@@ -138,6 +141,6 @@ func (c *Config) PrettyPrint() {
|
||||
}
|
||||
|
||||
// SetRoutePermissions sets the route permissions map
|
||||
func (c *Config) SetRoutePermissions(permissions map[string][]string) {
|
||||
func (c *CognitoConfig) SetRoutePermissions(permissions map[string][]string) {
|
||||
c.RoutePermissions = permissions
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// initiateLoginWithPKCE generates PKCE code challenge and redirects to Cognito
|
||||
// This function starts the OAuth authorization flow with PKCE
|
||||
func initiateLoginWithPKCE(c echo.Context, config *Config) error {
|
||||
func initiateLoginWithPKCE(c echo.Context, config *CognitoConfig) error {
|
||||
// Generate random state parameter to prevent CSRF
|
||||
state, err := generateRandomString(32)
|
||||
if err != nil {
|
||||
@@ -63,7 +63,7 @@ func initiateLoginWithPKCE(c echo.Context, config *Config) error {
|
||||
// exchangeCodeForTokensWithPKCE exchanges the authorization code for tokens using PKCE
|
||||
// Called during the OAuth callback flow to get tokens from the authorization code
|
||||
// Makes an HTTP request to Cognito's token endpoint to perform this exchange
|
||||
func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config Config) (*TokenResponse, error) {
|
||||
func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config CognitoConfig) (*TokenResponse, error) {
|
||||
data := url.Values{}
|
||||
data.Set("grant_type", "authorization_code")
|
||||
data.Set("client_id", config.ClientID)
|
||||
@@ -118,7 +118,7 @@ func exchangeCodeForTokensWithPKCE(authCode, codeVerifier string, config Config)
|
||||
// handleOAuthCallback handles the OAuth 2.0 authorization code flow callback with PKCE
|
||||
// Called when Cognito redirects back to our application with an authorization code
|
||||
// Exchanges the code for tokens, verifies them, and redirects to home page with token in cookie
|
||||
func handleOAuthCallback(c echo.Context, config *Config) error {
|
||||
func handleOAuthCallback(c echo.Context, config *CognitoConfig) error {
|
||||
// Extract the authorization code and state
|
||||
code := c.QueryParam("code")
|
||||
if code == "" {
|
||||
@@ -228,7 +228,7 @@ func handleOAuthCallback(c echo.Context, config *Config) error {
|
||||
}
|
||||
|
||||
// LogoutHandler handles the logout process by clearing the auth cookie
|
||||
func LogoutHandler(c echo.Context, config *Config) error {
|
||||
func LogoutHandler(c echo.Context, config *CognitoConfig) error {
|
||||
// Clear the token cookie
|
||||
cookie := new(http.Cookie)
|
||||
cookie.Name = "auth_token"
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
// TokenValidationMiddleware verifies JWT tokens efficiently
|
||||
// This is the primary middleware that handles both authentication and authorization
|
||||
// Applied to all routes to enforce security requirements
|
||||
func TokenValidationMiddleware(config *Config) echo.MiddlewareFunc {
|
||||
func TokenValidationMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
requestPath := c.Request().URL.Path
|
||||
@@ -106,7 +106,7 @@ func TokenValidationMiddleware(config *Config) echo.MiddlewareFunc {
|
||||
}
|
||||
|
||||
// JWTAuthMiddleware checks for JWT token in cookies and adds it to the Authorization header
|
||||
func JWTAuthMiddleware(config *Config) echo.MiddlewareFunc {
|
||||
func JWTAuthMiddleware(config *CognitoConfig) echo.MiddlewareFunc {
|
||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
// Skip for login and callback paths
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// verifyToken verifies the JWT token and returns its claims
|
||||
// Used during both OAuth callback and subsequent API requests with bearer token
|
||||
// Verifies signature, expiration, issuer, and other JWT claims
|
||||
func verifyToken(tokenString string, keySet jwk.Set, config Config) (map[string]interface{}, error) {
|
||||
func verifyToken(tokenString string, keySet jwk.Set, config CognitoConfig) (map[string]interface{}, error) {
|
||||
region := config.Region
|
||||
issuer := fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s", region, config.UserPoolID)
|
||||
|
||||
|
||||
@@ -84,14 +84,14 @@ func checkPermissions(path string, userGroups []string, routePermissions map[str
|
||||
return false, requiredGroups
|
||||
}
|
||||
|
||||
// Helper function to mask sensitive values
|
||||
// Used for logging sensitive information like client IDs
|
||||
func maskString(s string) string {
|
||||
if s == "" {
|
||||
return "<not set>"
|
||||
}
|
||||
if len(s) <= 8 {
|
||||
return "****"
|
||||
}
|
||||
return s[:4] + "..." + s[len(s)-4:]
|
||||
}
|
||||
//// Helper function to mask sensitive values
|
||||
//// Used for logging sensitive information like client IDs
|
||||
//func maskString(s string) string {
|
||||
// if s == "" {
|
||||
// return "<not set>"
|
||||
// }
|
||||
// if len(s) <= 8 {
|
||||
// return "****"
|
||||
// }
|
||||
// return s[:4] + "..." + s[len(s)-4:]
|
||||
//}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
package cognitoauth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CognitoConfig holds the configuration for AWS Cognito
|
||||
// Contains all necessary parameters to interact with Cognito endpoints
|
||||
type CognitoConfig struct {
|
||||
// annotated vars
|
||||
Region string `env:"COGNITO_REGION" envDefault:"us-east-2"`
|
||||
UserPoolID string `env:"COGNITO_USER_POOL_ID,required,notEmpty"`
|
||||
ClientSecret string `env:"COGNITO_CLIENT_SECRET,required,notEmpty"`
|
||||
Domain string `env:"COGNITO_DOMAIN,required,notEmpty"`
|
||||
ClientID string `env:"COGNITO_CLIENT_ID,required,notEmpty"`
|
||||
|
||||
RedirectURI string // URL where Cognito redirects after authentication
|
||||
TokenURL string // Cognito endpoint for token operations
|
||||
JwksURL string // URL for JSON Web Key Set (for token verification)
|
||||
AuthURL string // Cognito authorization endpoint for initiating login
|
||||
LoginPath string // Path for initiating login
|
||||
CallbackPath string // Path for OAuth callback
|
||||
HomePath string // Path for home page
|
||||
LogoutPath string // Path for logout
|
||||
AuthLogger *slog.Logger // Logger instance ( normally just the same logger as main)
|
||||
RoutePermissions map[string][]string // Map of routes to required permissions
|
||||
}
|
||||
|
||||
// InitializeConfig creates a new CognitoConfig from environment variables
|
||||
// and sets the values that are computed from other values
|
||||
func InitializeConfig(baseURL string, logger *slog.Logger) *CognitoConfig {
|
||||
// Set default logger if not provided
|
||||
if logger == nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// Set paths with defaults
|
||||
loginPath := os.Getenv("COGNITO_LOGIN_PATH")
|
||||
if loginPath == "" {
|
||||
loginPath = "/login"
|
||||
}
|
||||
|
||||
callbackPath := os.Getenv("COGNITO_CALLBACK_PATH")
|
||||
if callbackPath == "" {
|
||||
callbackPath = "/login-callback"
|
||||
}
|
||||
|
||||
homePath := os.Getenv("COGNITO_HOME_PATH")
|
||||
if homePath == "" {
|
||||
homePath = "/home"
|
||||
}
|
||||
|
||||
logoutPath := os.Getenv("COGNITO_LOGOUT_PATH")
|
||||
if logoutPath == "" {
|
||||
logoutPath = "/logout"
|
||||
}
|
||||
|
||||
// Get AWS region with fallback
|
||||
region := os.Getenv("AWS_REGION")
|
||||
if region == "" {
|
||||
region = "us-east-2" // Fallback to default value
|
||||
}
|
||||
|
||||
// Get User Pool ID with fallback
|
||||
userPoolID := os.Getenv("COGNITO_USER_POOL_ID")
|
||||
if userPoolID == "" {
|
||||
userPoolID = "" // No default, will need to be provided
|
||||
logger.Warn("COGNITO_USER_POOL_ID environment variable is not set")
|
||||
}
|
||||
|
||||
// Get Cognito domain
|
||||
domain := os.Getenv("COGNITO_DOMAIN")
|
||||
if domain == "" {
|
||||
//logger.Warn("COGNITO_DOMAIN environment variable is not set, using default")
|
||||
//// Use the user pool ID directly as part of the domain
|
||||
//domain = fmt.Sprintf("%s.auth.%s.amazoncognito.com", userPoolID, region)
|
||||
log.Fatalf("COGNITO_DOMAIN environment variable is not set")
|
||||
}
|
||||
|
||||
// Ensure domain format is correct (no protocol prefix)
|
||||
domain = strings.TrimPrefix(domain, "https://")
|
||||
domain = strings.TrimPrefix(domain, "http://")
|
||||
|
||||
// Sanitize base URL (remove trailing slash)
|
||||
baseURL = strings.TrimSuffix(baseURL, "/")
|
||||
|
||||
config := &CognitoConfig{
|
||||
ClientID: os.Getenv("COGNITO_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("COGNITO_CLIENT_SECRET"),
|
||||
RedirectURI: baseURL + callbackPath,
|
||||
TokenURL: fmt.Sprintf("https://%s/oauth2/token", domain),
|
||||
AuthURL: fmt.Sprintf("https://%s/oauth2/authorize", domain),
|
||||
JwksURL: fmt.Sprintf("https://cognito-idp.%s.amazonaws.com/%s/.well-known/jwks.json", region, userPoolID),
|
||||
UserPoolID: userPoolID,
|
||||
Region: region,
|
||||
LoginPath: loginPath,
|
||||
CallbackPath: callbackPath,
|
||||
HomePath: homePath,
|
||||
LogoutPath: logoutPath,
|
||||
AuthLogger: logger,
|
||||
RoutePermissions: make(map[string][]string),
|
||||
}
|
||||
|
||||
config.PrettyPrint()
|
||||
return config
|
||||
}
|
||||
|
||||
func (c *CognitoConfig) PrettyPrint() {
|
||||
fmt.Printf("CognitoConfig:\n")
|
||||
fmt.Printf(" ClientID: %s\n", c.ClientID)
|
||||
fmt.Printf(" ClientSecret: %s\n", c.ClientSecret)
|
||||
fmt.Printf(" RedirectURI: %s\n", c.RedirectURI)
|
||||
fmt.Printf(" TokenURL: %s\n", c.TokenURL)
|
||||
fmt.Printf(" AuthURL: %s\n", c.AuthURL)
|
||||
fmt.Printf(" JwksURL: %s\n", c.JwksURL)
|
||||
fmt.Printf(" UserPoolID: %s\n", c.UserPoolID)
|
||||
fmt.Printf(" Region: %s\n", c.Region)
|
||||
fmt.Printf(" LoginPath: %s\n", c.LoginPath)
|
||||
fmt.Printf(" CallbackPath: %s\n", c.CallbackPath)
|
||||
fmt.Printf(" HomePath: %s\n", c.HomePath)
|
||||
fmt.Printf(" LogoutPath: %s\n", c.LogoutPath)
|
||||
|
||||
// pretty print the route permissions map
|
||||
for route, permissions := range c.RoutePermissions {
|
||||
fmt.Printf(" Route: %s, Permissions: %v\n", route, permissions)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// SetRoutePermissions sets the route permissions map
|
||||
func (c *CognitoConfig) SetRoutePermissions(permissions map[string][]string) {
|
||||
c.RoutePermissions = permissions
|
||||
}
|
||||
@@ -4,12 +4,11 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"queryorchestration/internal/serviceconfig/cognitoauth"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"queryorchestration/internal/serviceconfig/rbac"
|
||||
|
||||
"queryorchestration/internal/serviceconfig/aws"
|
||||
"queryorchestration/internal/serviceconfig/database"
|
||||
"queryorchestration/internal/serviceconfig/logger"
|
||||
@@ -33,7 +32,8 @@ type BaseConfig struct {
|
||||
|
||||
Pwd string `env:"PWD,required,notEmpty"`
|
||||
|
||||
rbac.AuthConfig
|
||||
//rbac.AuthConfig
|
||||
cognitoauth.CognitoConfig
|
||||
logger.LogConfig
|
||||
observability.ObsConfig
|
||||
database.DBConfig
|
||||
@@ -52,7 +52,7 @@ type ConfigProvider interface {
|
||||
logger.ConfigProvider
|
||||
aws.ConfigProvider
|
||||
queue.ConfigProvider
|
||||
rbac.ConfigProvider
|
||||
//rbac.ConfigProvider
|
||||
}
|
||||
|
||||
// Configuration Methods
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"queryorchestration/internal/rbac"
|
||||
)
|
||||
|
||||
// AuthConfig holds the RBAC configuration and KeyProvider
|
||||
type AuthConfig struct {
|
||||
// cognito or local
|
||||
AuthType string `env:"RBAC_PROVIDER" envDefault:"local"`
|
||||
// Cognito Config
|
||||
CognitoRegion string `env:"COGNITO_REGION" envDefault:"us-east-1"`
|
||||
CognitoUserPoolID string `env:"COGNITO_USER_POOL_ID"`
|
||||
// Local Key Config
|
||||
PrivateKeyPath string `env:"RBAC_PRIVATE_KEY_PATH" envDefault:"./public_key.pem"`
|
||||
PublicKeyPath string `env:"RBAC_PUBLIC_KEY_PATH" envDefault:"./private_key.pem"`
|
||||
|
||||
// Actual provider - can be either local or cognito
|
||||
AuthProvider rbac.KeyProvider
|
||||
}
|
||||
|
||||
// ConfigProvider interface defines the methods required for RBAC configuration
|
||||
type ConfigProvider interface {
|
||||
//Initialize() error
|
||||
GetKeyProvider() rbac.KeyProvider
|
||||
PrintAuthConfig()
|
||||
}
|
||||
|
||||
// GetKeyProvider returns the configured KeyProvider
|
||||
func (r *AuthConfig) GetKeyProvider() rbac.KeyProvider {
|
||||
return r.AuthProvider
|
||||
}
|
||||
|
||||
func InitializeAuthProvider(config *AuthConfig) error {
|
||||
return config.InitializeAuthProvider()
|
||||
}
|
||||
|
||||
// InitializeAuthProvider initializes the appropriate KeyProvider based on configuration
|
||||
func (r *AuthConfig) InitializeAuthProvider() error {
|
||||
switch r.AuthType {
|
||||
case "local":
|
||||
if r.PrivateKeyPath == "" || r.PublicKeyPath == "" {
|
||||
return fmt.Errorf("local key provider requires both private and public key paths")
|
||||
}
|
||||
r.AuthProvider = rbac.NewLocalKeyProvider(r.PrivateKeyPath, r.PublicKeyPath)
|
||||
slog.Info("RBAC initialized with local key provider")
|
||||
|
||||
case "cognito":
|
||||
if r.CognitoUserPoolID == "" {
|
||||
return fmt.Errorf("cognito provider requires user pool ID")
|
||||
}
|
||||
r.AuthProvider = rbac.NewCognitoKeyProvider(r.CognitoRegion, r.CognitoUserPoolID)
|
||||
slog.Info("RBAC initialized with Cognito key provider")
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown RBAC provider type: %s", r.AuthType)
|
||||
}
|
||||
r.PrintAuthConfig()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PrintAuthConfig logs the current configuration, masking sensitive values
|
||||
func (r *AuthConfig) PrintAuthConfig() {
|
||||
slog.Info("RBAC AuthConfig:",
|
||||
"provider_type", r.AuthType,
|
||||
"region", r.CognitoRegion,
|
||||
"user_pool_id", r.CognitoUserPoolID,
|
||||
"private_key_path", r.PrivateKeyPath,
|
||||
"public_key_path", r.PublicKeyPath,
|
||||
)
|
||||
}
|
||||
|
||||
// Validate checks if the configuration is valid
|
||||
func (r *AuthConfig) Validate() error {
|
||||
switch r.AuthType {
|
||||
case "local":
|
||||
if r.PrivateKeyPath == "" || r.PublicKeyPath == "" {
|
||||
return fmt.Errorf("local key provider requires both private and public key paths")
|
||||
}
|
||||
case "cognito":
|
||||
if r.CognitoUserPoolID == "" {
|
||||
return fmt.Errorf("cognito provider requires user pool ID")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("invalid provider type: %s", r.AuthType)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
package rbac_test
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"queryorchestration/internal/serviceconfig/rbac"
|
||||
)
|
||||
|
||||
// TestGetKeyProvider tests the GetKeyProvider method
|
||||
func TestGetKeyProvider(t *testing.T) {
|
||||
// Create a temporary directory for test key files
|
||||
tmpDir, err := os.MkdirTemp("", "rbac-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer func(path string) {
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to remove temp dir: %v", err)
|
||||
}
|
||||
}(tmpDir)
|
||||
|
||||
// Create temporary key files with minimal PEM format
|
||||
privateKeyPath := tmpDir + "/private_key.pem"
|
||||
publicKeyPath := tmpDir + "/public_key.pem"
|
||||
|
||||
// Write minimal PEM content to the key files
|
||||
// These are not valid RSA keys, but that doesn't matter for this test
|
||||
// as we're just testing that the config passes the paths correctly
|
||||
err = os.WriteFile(privateKeyPath, []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA0Gzk05Pbnb12O7O+vCrwY9oMsEsKkJZ1hnMvP4JXOa0beJAw
|
||||
DgYKAAAAAAAAAAA=
|
||||
-----END RSA PRIVATE KEY-----`), 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write private key: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(publicKeyPath, []byte(`-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Gzk05Pbnb12O7O+vCrw
|
||||
Y9oMsEsKkJZ1hnMvP4JXOa0beJAwOJosFh5kAAs=
|
||||
-----END PUBLIC KEY-----`), 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write public key: %v", err)
|
||||
}
|
||||
|
||||
// Create an AuthConfig with local provider
|
||||
config := &rbac.AuthConfig{
|
||||
AuthType: "local",
|
||||
PrivateKeyPath: privateKeyPath,
|
||||
PublicKeyPath: publicKeyPath,
|
||||
}
|
||||
|
||||
// Since we can't successfully load invalid keys, we'll just test the GetKeyProvider method directly
|
||||
// without initializing the provider
|
||||
config.AuthProvider = &mockKeyProvider{} // Use our mock key provider
|
||||
|
||||
// Test GetKeyProvider
|
||||
provider := config.GetKeyProvider()
|
||||
assert.NotNil(t, provider, "GetKeyProvider should return a non-nil provider")
|
||||
assert.Equal(t, &mockKeyProvider{}, provider, "Provider should match what we set")
|
||||
}
|
||||
|
||||
// TestInitializeAuthProvider tests the InitializeAuthProvider function
|
||||
func TestInitializeAuthProvider(t *testing.T) {
|
||||
// Create a temporary directory for test key files
|
||||
tmpDir, err := os.MkdirTemp("", "rbac-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer func(path string) {
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to remove temp dir: %v", err)
|
||||
}
|
||||
}(tmpDir)
|
||||
|
||||
// Create temporary key files
|
||||
privateKeyPath := tmpDir + "/private_key.pem"
|
||||
publicKeyPath := tmpDir + "/public_key.pem"
|
||||
|
||||
// Write some dummy content to the key files - these are NOT valid keys
|
||||
err = os.WriteFile(privateKeyPath, []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA0Gzk05Pbnb12O7O+vCrwY9oMsEsKkJZ1hnMvP4JXOa0beJAw
|
||||
DgYKAAAAAAAAAAA=
|
||||
-----END RSA PRIVATE KEY-----`), 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write private key: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(publicKeyPath, []byte(`-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Gzk05Pbnb12O7O+vCrw
|
||||
Y9oMsEsKkJZ1hnMvP4JXOa0beJAwOJosFh5kAAs=
|
||||
-----END PUBLIC KEY-----`), 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write public key: %v", err)
|
||||
}
|
||||
|
||||
// Test cases for InitializeAuthProvider
|
||||
testCases := []struct {
|
||||
name string
|
||||
config *rbac.AuthConfig
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "Missing local key paths",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "local",
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing Cognito user pool ID",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "cognito",
|
||||
CognitoRegion: "us-east-1",
|
||||
// Missing user pool ID
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid auth type",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "invalid",
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := rbac.InitializeAuthProvider(tc.config)
|
||||
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAuthConfigInitializeAuthProvider tests the AuthConfig method version of InitializeAuthProvider
|
||||
func TestAuthConfigInitializeAuthProvider(t *testing.T) {
|
||||
// Create a temporary directory for test key files
|
||||
tmpDir, err := os.MkdirTemp("", "rbac-test")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer func(path string) {
|
||||
err := os.RemoveAll(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to remove temp dir: %v", err)
|
||||
}
|
||||
}(tmpDir)
|
||||
|
||||
// Create temporary key files
|
||||
privateKeyPath := tmpDir + "/private_key.pem"
|
||||
publicKeyPath := tmpDir + "/public_key.pem"
|
||||
|
||||
// Write dummy PEM content to the key files
|
||||
err = os.WriteFile(privateKeyPath, []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA0Gzk05Pbnb12O7O+vCrwY9oMsEsKkJZ1hnMvP4JXOa0beJAw
|
||||
DgYKAAAAAAAAAAA=
|
||||
-----END RSA PRIVATE KEY-----`), 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write private key: %v", err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(publicKeyPath, []byte(`-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Gzk05Pbnb12O7O+vCrw
|
||||
Y9oMsEsKkJZ1hnMvP4JXOa0beJAwOJosFh5kAAs=
|
||||
-----END PUBLIC KEY-----`), 0600)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to write public key: %v", err)
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
config *rbac.AuthConfig
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "Supplied local key paths",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "local",
|
||||
// Missing key paths
|
||||
PublicKeyPath: publicKeyPath,
|
||||
PrivateKeyPath: privateKeyPath,
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Missing Cognito user pool ID",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "cognito",
|
||||
CognitoRegion: "us-east-1",
|
||||
// Missing user pool ID
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid auth type",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "invalid",
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.config.InitializeAuthProvider()
|
||||
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestPrintAuthConfig tests the PrintAuthConfig method
|
||||
func TestPrintAuthConfig(t *testing.T) {
|
||||
// Create an AuthConfig with some test values
|
||||
config := &rbac.AuthConfig{
|
||||
AuthType: "cognito",
|
||||
CognitoRegion: "us-east-1",
|
||||
CognitoUserPoolID: "us-east-1_TestPool",
|
||||
PrivateKeyPath: "/path/to/private.pem",
|
||||
PublicKeyPath: "/path/to/public.pem",
|
||||
}
|
||||
|
||||
// Call PrintAuthConfig - there's no return value, so we're just ensuring it doesn't panic
|
||||
config.PrintAuthConfig()
|
||||
}
|
||||
|
||||
// TestValidate tests the Validate method
|
||||
func TestValidate(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
config *rbac.AuthConfig
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "Valid local config",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "local",
|
||||
PrivateKeyPath: "/path/to/private.pem",
|
||||
PublicKeyPath: "/path/to/public.pem",
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Valid Cognito config",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "cognito",
|
||||
CognitoRegion: "us-east-1",
|
||||
CognitoUserPoolID: "us-east-1_TestPool",
|
||||
},
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid auth type",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "invalid",
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing local key paths",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "local",
|
||||
// Missing key paths
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "Missing Cognito user pool ID",
|
||||
config: &rbac.AuthConfig{
|
||||
AuthType: "cognito",
|
||||
CognitoRegion: "us-east-1",
|
||||
// Missing user pool ID
|
||||
},
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := tc.config.Validate()
|
||||
|
||||
if tc.expectError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Mock key provider for testing
|
||||
type mockKeyProvider struct{}
|
||||
|
||||
func (m *mockKeyProvider) GetPublicKey(kid string) (*rsa.PublicKey, error) {
|
||||
return nil, nil
|
||||
}
|
||||
Reference in New Issue
Block a user