diff --git a/cmd/cognito_test/jwtstuff/main.go b/cmd/cognito_test/jwtstuff/main.go index 9c09e4c6..ec23883a 100644 --- a/cmd/cognito_test/jwtstuff/main.go +++ b/cmd/cognito_test/jwtstuff/main.go @@ -2,46 +2,32 @@ package main import ( "fmt" - "os" - "sort" - + "log" "queryorchestration/internal/rbac" // Replace with your actual module name + "queryorchestration/internal/serviceconfig" ) -//type DummyServiceConfig struct { -// service.BaseConfig -//} - -func PrintEnvironmentVariables() { - // Get all environment variables - envVars := os.Environ() - - // Sort them for consistent output - sort.Strings(envVars) - - // Print each variable in "key = value" format - for _, env := range envVars { - // Find the first equals sign to split the string - for i := 0; i < len(env); i++ { - if env[i] == '=' { - key := env[:i] - value := env[i+1:] - fmt.Printf("%s = %s\n", key, value) - break - } - } - } -} - +// for testing run like `LOG_LEVEL=INFO go run main.go` func main() { - PrintEnvironmentVariables() - //cfg := &DummyServiceConfig{} - //errGettingConfig := serviceconfig.InitializeConfig(cfg) - //if errGettingConfig != nil { - // log.Fatal(errGettingConfig) - //} + + cfg := &serviceconfig.BaseConfig{} + errGettingConfig := serviceconfig.InitializeConfig(cfg) + if errGettingConfig != nil { + log.Fatal(errGettingConfig) + } + + errorInitializingAuthProvider := cfg.InitializeAuthProvider() + if errorInitializingAuthProvider != nil { + log.Fatal(errorInitializingAuthProvider) + } + + cfg.PrintConfig("secret") // use the config here for the key provider parameters // Example with local key provider for testing + if cfg.AuthProvider == nil { + log.Fatal("AuthProvider is nil") + } + localProvider := rbac.NewLocalKeyProvider("private_key.pem", "public_key.pem") // Generate a mock JWT with multiple Cognito groups diff --git a/cmd/queryService/main.go b/cmd/queryService/main.go index 2c6f8ee6..21f2c01f 100644 --- a/cmd/queryService/main.go +++ b/cmd/queryService/main.go @@ -19,6 +19,8 @@ import ( "log/slog" "os" + "queryorchestration/internal/serviceconfig" + queryservice "queryorchestration/api/queryService" "queryorchestration/internal/client" "queryorchestration/internal/collector" @@ -32,6 +34,7 @@ import ( service "queryorchestration/internal/server/service" "queryorchestration/internal/serviceconfig/queue/clientsync" "queryorchestration/internal/serviceconfig/queue/queryversionsync" + "queryorchestration/internal/serviceconfig/rbac" "github.com/getkin/kin-openapi/openapi3" _ "github.com/lib/pq" @@ -92,6 +95,20 @@ func main() { return swagger, nil } + // this must be done before the rbac.InitializeAuthProvider + errInitializingConfig := serviceconfig.InitializeConfig(cfg) + if errInitializingConfig != nil { + slog.Error(errInitializingConfig.Error()) + os.Exit(1) + } + + // 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) + } + server, err := service.New(ctx, cfg) if err != nil { slog.Error(err.Error()) diff --git a/internal/serviceconfig/common.go b/internal/serviceconfig/common.go index 614feac6..b0a112d9 100644 --- a/internal/serviceconfig/common.go +++ b/internal/serviceconfig/common.go @@ -28,18 +28,21 @@ import ( // BaseConfig provides common configuration fields and functionality // that can be embedded in service-specific configs. type BaseConfig struct { - rbac.AuthConfig - logger.LogConfig - observability.ObsConfig - database.DBConfig - aws.AWSConfig - queue.QueueConfig + // Always place non struct fields at the top of the struct + // this is a requirement for the PrintConfig function. // miscellaneous fields uncategorized // PWD will replace the BASE_PATH env var Pwd string `env:"PWD,required,notEmpty"` // BASE_PATH will override the PWD env var if present BasePath string `env:"BASE_PATH"` + + rbac.AuthConfig + logger.LogConfig + observability.ObsConfig + database.DBConfig + aws.AWSConfig + queue.QueueConfig } // Interfaces @@ -110,7 +113,7 @@ func getBaseConfig(cfg ConfigProvider) *BaseConfig { } var ( - initOnce sync.Once + initOnce = sync.Once{} initErr error ) @@ -185,25 +188,35 @@ func (b *BaseConfig) SetAWSConfig(cfg *aws.AWSConfig) { } func (b *BaseConfig) PrintConfig(prefixSecret string) { - b.printConfigRecursive(reflect.ValueOf(b), "", prefixSecret, make(map[uintptr]bool)) + visited := make(map[uintptr]bool) + b.printConfigRecursive(reflect.ValueOf(b).Elem(), "", prefixSecret, visited) } func (b *BaseConfig) printConfigRecursive(val reflect.Value, prefix string, prefixSecret string, visited map[uintptr]bool) { - // Handle pointer dereference + // Skip non-structs and nil pointers if val.Kind() == reflect.Ptr { + if val.IsNil() { + return + } val = val.Elem() } - // Prevent infinite recursion using the memory address - // Recommended way to get the uintptr - addr := uintptr(val.Addr().UnsafePointer()) - if visited[addr] { + if val.Kind() != reflect.Struct { return } - visited[addr] = true + + // Check for cycles, but only for addressable values + if val.CanAddr() { + addr := uintptr(val.Addr().UnsafePointer()) + if visited[addr] { + return + } + visited[addr] = true + } typ := val.Type() + // Process all fields including embedded ones for i := 0; i < val.NumField(); i++ { field := val.Field(i) fieldType := typ.Field(i) @@ -214,50 +227,54 @@ func (b *BaseConfig) printConfigRecursive(val reflect.Value, prefix string, pref } fieldName := fieldType.Name - fullPath := prefix + fieldName - - // Check for env tag - envTag := fieldType.Tag.Get("env") - if envTag == "" { - // If this is a struct, we still need to recurse into it - // as it might contain fields with env tags - if field.Kind() == reflect.Struct { - b.printConfigRecursive(field, fullPath+".", prefixSecret, visited) - } + // Handle anonymous (embedded) fields specially + if fieldType.Anonymous { + // For embedded fields, recurse without adding to the path + // This is key - we want to process the embedded struct's fields directly + b.printConfigRecursive(field, prefix, prefixSecret, visited) continue } - // Handle different kinds of fields - switch field.Kind() { - case reflect.Struct: - // If it's a struct AND has an env tag, print it and recurse - var valueStr string - if field.CanInterface() { - valueStr = fmt.Sprintf("%+v", field.Interface()) - } + fullPath := prefix + fieldName + envTag := fieldType.Tag.Get("env") - b.Logger.Info("Struct Config value", - "key", fullPath, - "value", valueStr) - b.printConfigRecursive(field, fullPath+".", prefixSecret, visited) - default: - var valueStr string - if field.Kind() == reflect.String { - valueStr = field.String() - } else { - valueStr = fmt.Sprintf("%v", field.Interface()) - } - - // Mask sensitive values - if strings.Contains(strings.ToLower(fieldName), strings.ToLower(prefixSecret)) { - if len(valueStr) > 3 { - valueStr = valueStr[:3] + "..." + // Process fields with env tags + if envTag != "" { + if field.Kind() == reflect.Struct { + // For structs with env tags, log the struct and recurse + var valueStr string + if field.CanInterface() { + valueStr = fmt.Sprintf("%+v", field.Interface()) } - } - b.Logger.Info("Config value", - "key", fullPath, - "value", valueStr) + b.Logger.Info("Struct Config value", + "key", fullPath, + "value", valueStr) + } else { + // For non-struct fields with env tags + var valueStr string + if field.Kind() == reflect.String { + valueStr = field.String() + } else { + valueStr = fmt.Sprintf("%v", field.Interface()) + } + + // Mask sensitive values + if strings.Contains(strings.ToLower(fieldName), strings.ToLower(prefixSecret)) { + if len(valueStr) > 3 { + valueStr = valueStr[:3] + "..." + } + } + + b.Logger.Info("Config value", + "key", fullPath, + "value", valueStr) + } + } + + // Recurse into structs regardless of env tag + if field.Kind() == reflect.Struct { + b.printConfigRecursive(field, fullPath+".", prefixSecret, visited) } } } diff --git a/internal/serviceconfig/rbac/config.go b/internal/serviceconfig/rbac/config.go index 9c072d27..287622c6 100644 --- a/internal/serviceconfig/rbac/config.go +++ b/internal/serviceconfig/rbac/config.go @@ -9,51 +9,16 @@ import ( // AuthConfig holds the RBAC configuration and KeyProvider type AuthConfig struct { - AuthProvider rbac.KeyProvider - AuthType string `env:"RBAC_PROVIDER" envDefault:"cognito"` + AuthType string `env:"RBAC_PROVIDER" envDefault:"cognito"` // 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"` PublicKeyPath string `env:"RBAC_PUBLIC_KEY_PATH"` -} -//func IdentifyAuthProviderAndCreate(config rbac.AuthConfig) (KeyProvider, error) { -// var keyProvider KeyProvider -// if config.AuthType == "local" { -// keyProvider = NewLocalKeyProvider(config.PrivateKeyPath, config.PublicKeyPath) -// } else if config.AuthType == "cognito" { -// keyProvider = NewCognitoKeyProvider(config.CognitoRegion, config.CognitoUserPoolID) -// } -// if keyProvider == nil { -// return nil, fmt.Errorf("invalid auth provider: %s", config.AuthProvider) -// } -// return keyProvider, nil -//} - -// NewAuthConfig creates a new AuthConfig with the specified provider type -func NewAuthConfig(providerType string) (*AuthConfig, error) { - config := &AuthConfig{ - AuthType: providerType, - } - if err := config.Initialize(); err != nil { - return nil, fmt.Errorf("failed to initialize auth config: %w", err) - } - return config, nil -} - -// NewLocalAuthConfig creates a new AuthConfig configured for local key provider -func NewLocalAuthConfig(privateKeyPath, publicKeyPath string) (*AuthConfig, error) { - config := &AuthConfig{ - AuthType: "local", - PrivateKeyPath: privateKeyPath, - PublicKeyPath: publicKeyPath, - } - if err := config.Initialize(); err != nil { - return nil, fmt.Errorf("failed to initialize local auth config: %w", err) - } - return config, nil + // Actual provider - can be either local or cognito + AuthProvider rbac.KeyProvider } // ConfigProvider interface defines the methods required for RBAC configuration @@ -68,8 +33,12 @@ func (r *AuthConfig) GetKeyProvider() rbac.KeyProvider { return r.AuthProvider } -// Initialize initializes the appropriate KeyProvider based on configuration -func (r *AuthConfig) Initialize() error { +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 == "" { diff --git a/internal/serviceconfig/rbac/testutil.go b/internal/serviceconfig/rbac/testutil.go index 145a9d4b..bd53d693 100644 --- a/internal/serviceconfig/rbac/testutil.go +++ b/internal/serviceconfig/rbac/testutil.go @@ -13,7 +13,7 @@ func TestKeyProvider(t *testing.T, privateKeyPath, publicKeyPath string) rbac.Ke PrivateKeyPath: privateKeyPath, PublicKeyPath: publicKeyPath, } - err := config.Initialize() + err := config.InitializeAuthProvider() if err != nil { t.Fatalf("Failed to initialize auth config: %v", err) }