- Add API server with WebSocket support and REST endpoints - Implement authentication system with API keys and permissions - Add task queue system with Redis backend and error handling - Include storage layer with database migrations and schemas - Add comprehensive logging, metrics, and telemetry - Implement security middleware and network utilities - Add experiment management and container orchestration - Include configuration management with smart defaults
100 lines
2.6 KiB
Go
100 lines
2.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// ValidateAuthConfig enforces authentication requirements
|
|
func (c *AuthConfig) ValidateAuthConfig() error {
|
|
// Check if we're in production environment
|
|
isProduction := os.Getenv("FETCH_ML_ENV") == "production"
|
|
|
|
if isProduction {
|
|
if !c.Enabled {
|
|
return fmt.Errorf("authentication must be enabled in production environment")
|
|
}
|
|
|
|
if len(c.APIKeys) == 0 {
|
|
return fmt.Errorf("at least one API key must be configured in production")
|
|
}
|
|
|
|
// Ensure at least one admin user exists
|
|
hasAdmin := false
|
|
for _, entry := range c.APIKeys {
|
|
if entry.Admin {
|
|
hasAdmin = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasAdmin {
|
|
return fmt.Errorf("at least one admin user must be configured in production")
|
|
}
|
|
|
|
// Check for insecure development override
|
|
if os.Getenv("FETCH_ML_ALLOW_INSECURE_AUTH") == "1" {
|
|
log.Printf("WARNING: FETCH_ML_ALLOW_INSECURE_AUTH is enabled in production - this is insecure")
|
|
}
|
|
}
|
|
|
|
// Validate API key format
|
|
for username, entry := range c.APIKeys {
|
|
if string(username) == "" {
|
|
return fmt.Errorf("empty username not allowed")
|
|
}
|
|
|
|
if entry.Hash == "" {
|
|
return fmt.Errorf("user %s has empty API key hash", username)
|
|
}
|
|
|
|
// Validate hash format (should be 64 hex chars for SHA256)
|
|
if len(entry.Hash) != 64 {
|
|
return fmt.Errorf("user %s has invalid API key hash format", username)
|
|
}
|
|
|
|
// Check hash contains only hex characters
|
|
for _, char := range entry.Hash {
|
|
if !((char >= '0' && char <= '9') || (char >= 'a' && char <= 'f') || (char >= 'A' && char <= 'F')) {
|
|
return fmt.Errorf("user %s has invalid API key hash characters", username)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CheckConfigFilePermissions ensures config files have secure permissions
|
|
func CheckConfigFilePermissions(configPath string) error {
|
|
info, err := os.Stat(configPath)
|
|
if err != nil {
|
|
return fmt.Errorf("cannot stat config file: %w", err)
|
|
}
|
|
|
|
// Check file permissions (should be 600 or 640)
|
|
perm := info.Mode().Perm()
|
|
if perm&0077 != 0 {
|
|
return fmt.Errorf("config file %s has insecure permissions: %o (should be 600 or 640)", configPath, perm)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SanitizeConfig removes sensitive information for logging
|
|
func (c *AuthConfig) SanitizeConfig() map[string]interface{} {
|
|
sanitized := map[string]interface{}{
|
|
"enabled": c.Enabled,
|
|
"users": make(map[string]interface{}),
|
|
}
|
|
|
|
for username := range c.APIKeys {
|
|
sanitized["users"].(map[string]interface{})[string(username)] = map[string]interface{}{
|
|
"admin": c.APIKeys[username].Admin,
|
|
"hash": strings.Repeat("*", 8) + "...", // Show only prefix
|
|
}
|
|
}
|
|
|
|
return sanitized
|
|
}
|