package auth import ( "flag" "fmt" "log" "os" "strings" ) // AuthFlags holds authentication-related command line flags type AuthFlags struct { APIKey string APIKeyFile string ConfigFile string EnableAuth bool ShowHelp bool } // ParseAuthFlags parses authentication command line flags func ParseAuthFlags() *AuthFlags { flags := &AuthFlags{} flag.StringVar(&flags.APIKey, "api-key", "", "API key for authentication") flag.StringVar(&flags.APIKeyFile, "api-key-file", "", "Path to file containing API key") flag.StringVar(&flags.ConfigFile, "config", "", "Configuration file path") flag.BoolVar(&flags.EnableAuth, "enable-auth", false, "Enable authentication") flag.BoolVar(&flags.ShowHelp, "auth-help", false, "Show authentication help") // Custom help flag that doesn't exit flag.Usage = func() {} flag.Parse() return flags } // GetAPIKeyFromSources gets API key from multiple sources in priority order func GetAPIKeyFromSources(flags *AuthFlags) string { // 1. Command line flag (highest priority) if flags.APIKey != "" { return flags.APIKey } // 2. Explicit file flag if flags.APIKeyFile != "" { contents, readErr := os.ReadFile(flags.APIKeyFile) if readErr == nil { return strings.TrimSpace(string(contents)) } log.Printf("Warning: Could not read API key file %s: %v", flags.APIKeyFile, readErr) } // 3. Environment variable if envKey := os.Getenv("FETCH_ML_API_KEY"); envKey != "" { return envKey } // 4. File-based key (for automated scripts) if fileKey := os.Getenv("FETCH_ML_API_KEY_FILE"); fileKey != "" { content, err := os.ReadFile(fileKey) if err == nil { return strings.TrimSpace(string(content)) } log.Printf("Warning: Could not read API key file %s: %v", fileKey, err) } return "" } // ValidateAuthFlags validates parsed authentication flags func ValidateAuthFlags(flags *AuthFlags) error { if flags.ShowHelp { PrintAuthHelp() os.Exit(0) } if flags.APIKeyFile != "" { if _, err := os.Stat(flags.APIKeyFile); err != nil { return fmt.Errorf("api key file not found: %s", flags.APIKeyFile) } if err := CheckConfigFilePermissions(flags.APIKeyFile); err != nil { log.Printf("Warning: %v", err) } } // If config file is specified, check if it exists if flags.ConfigFile != "" { if _, err := os.Stat(flags.ConfigFile); err != nil { return fmt.Errorf("config file not found: %s", flags.ConfigFile) } // Check file permissions if err := CheckConfigFilePermissions(flags.ConfigFile); err != nil { log.Printf("Warning: %v", err) } } return nil } // PrintAuthHelp prints authentication-specific help func PrintAuthHelp() { fmt.Println("Authentication Options:") fmt.Println(" --api-key API key for authentication") fmt.Println(" --api-key-file Read API key from file") fmt.Println(" --config Configuration file path") fmt.Println(" --enable-auth Enable authentication (if disabled)") fmt.Println(" --auth-help Show this help") fmt.Println() fmt.Println("Environment Variables:") fmt.Println(" FETCH_ML_API_KEY API key for authentication") fmt.Println(" FETCH_ML_API_KEY_FILE File containing API key") fmt.Println(" FETCH_ML_ENV Environment (development/production)") fmt.Println(" FETCH_ML_ALLOW_INSECURE_AUTH Allow insecure auth (dev only)") fmt.Println() fmt.Println("Security Notes:") fmt.Println(" - API keys in command line may be visible in process lists") fmt.Println(" - Environment variables are preferred for automated scripts") fmt.Println(" - File-based keys should have restricted permissions (600)") fmt.Println(" - Authentication is mandatory in production environments") }