diff --git a/cmd/oidcgate/config.go b/cmd/oidcgate/config.go index 32f76d8..83935aa 100644 --- a/cmd/oidcgate/config.go +++ b/cmd/oidcgate/config.go @@ -4,8 +4,8 @@ import ( "encoding/json" "fmt" "os" - "strconv" "strings" + "unicode" "github.com/lukaszraczylo/traefikoidc" "gopkg.in/yaml.v3" @@ -78,6 +78,10 @@ func Load(path string) (*Config, error) { applyEnvOverrides(cfg) applyDefaults(cfg) + if cfg.Listen == "" { + return nil, fmt.Errorf("config: missing required 'listen' (or OIDCGATE_LISTEN env var)") + } + // Force standalone semantics: trust X-Forwarded-Uri. cfg.OIDC.TrustForwardedURI = true return cfg, nil @@ -152,8 +156,6 @@ func setScalarField(cfg *Config, field, value string) { case "CACertPEM": cfg.OIDC.CACertPEM = value } - // strconv reserved for future int/bool fields when added to the allow-list. - _ = strconv.Atoi } func applyDefaults(cfg *Config) { @@ -170,22 +172,20 @@ func applyDefaults(cfg *Config) { // Multi-letter acronyms keep their grouping: "OIDCEndSessionURL" → // "OIDC_END_SESSION_URL", "CACertPEM" → "CA_CERT_PEM". func camelToSnakeUpper(s string) string { + runes := []rune(s) var b strings.Builder - for i, r := range s { + for i, r := range runes { if i > 0 && isUpper(r) { - prev := rune(s[i-1]) + prev := runes[i-1] next := rune(0) - if i+1 < len(s) { - next = rune(s[i+1]) + if i+1 < len(runes) { + next = runes[i+1] } if !isUpper(prev) || (next != 0 && !isUpper(next)) { b.WriteByte('_') } } - if r >= 'a' && r <= 'z' { - r -= 32 - } - b.WriteRune(r) + b.WriteRune(unicode.ToUpper(r)) } return b.String() } diff --git a/cmd/oidcgate/config_test.go b/cmd/oidcgate/config_test.go index 5c5ecf7..aa976a5 100644 --- a/cmd/oidcgate/config_test.go +++ b/cmd/oidcgate/config_test.go @@ -106,3 +106,32 @@ func TestLoad_MissingFile(t *testing.T) { t.Fatal("expected error for missing file") } } + +func TestLoad_NestedStructRoundTrip(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "config.yaml") + if err := os.WriteFile(path, []byte(` +listen: ":8080" +providerURL: "https://idp.example" +clientID: "abc" +clientSecret: "secret" +sessionEncryptionKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" +callbackURL: "/oauth2/callback" +logoutURL: "/oauth2/logout" +redis: + address: "redis:6379" + password: "redispw" +`), 0o600); err != nil { + t.Fatal(err) + } + cfg, err := Load(path) + if err != nil { + t.Fatal(err) + } + if cfg.OIDC.Redis == nil { + t.Fatal("redis block should populate cfg.OIDC.Redis") + } + if cfg.OIDC.Redis.Address != "redis:6379" { + t.Errorf("redis address: want redis:6379, got %q", cfg.OIDC.Redis.Address) + } +}