fetch_ml/internal/auth/flags.go
Jeremie Fraeys ea15af1833 Fix multi-user authentication and clean up debug code
- Fix YAML tags in auth config struct (json -> yaml)
- Update CLI configs to use pre-hashed API keys
- Remove double hashing in WebSocket client
- Fix port mapping (9102 -> 9103) in CLI commands
- Update permission keys to use jobs:read, jobs:create, etc.
- Clean up all debug logging from CLI and server
- All user roles now authenticate correctly:
  * Admin: Can queue jobs and see all jobs
  * Researcher: Can queue jobs and see own jobs
  * Analyst: Can see status (read-only access)

Multi-user authentication is now fully functional.
2025-12-06 12:35:32 -05:00

124 lines
3.7 KiB
Go

package auth
import (
"flag"
"fmt"
"log"
"os"
"strings"
"github.com/jfraeys/fetch_ml/internal/fileutil"
)
// Flags holds authentication-related command line flags
type Flags struct {
APIKey string
APIKeyFile string
ConfigFile string
EnableAuth bool
ShowHelp bool
}
// ParseAuthFlags parses authentication command line flags
func ParseAuthFlags() *Flags {
flags := &Flags{}
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 *Flags) 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 := fileutil.SecureFileRead(fileKey)
if err == nil {
return strings.TrimSpace(string(content))
}
log.Printf("Warning: Could not read API key file %s: %v", fileKey, err)
}
return ""
}
// ValidateFlags validates parsed authentication flags
func ValidateFlags(flags *Flags) 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 <key> API key for authentication")
fmt.Println(" --api-key-file <path> Read API key from file")
fmt.Println(" --config <file> 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")
}