fix(oidcgate): quality fixes — rune-safe snake-upper, drop dead import, listen validation, nested-struct test

This commit is contained in:
2026-05-19 13:40:24 +01:00
parent 17c28fd574
commit 8f458b4f6e
2 changed files with 40 additions and 11 deletions
+11 -11
View File
@@ -4,8 +4,8 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
"unicode"
"github.com/lukaszraczylo/traefikoidc" "github.com/lukaszraczylo/traefikoidc"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -78,6 +78,10 @@ func Load(path string) (*Config, error) {
applyEnvOverrides(cfg) applyEnvOverrides(cfg)
applyDefaults(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. // Force standalone semantics: trust X-Forwarded-Uri.
cfg.OIDC.TrustForwardedURI = true cfg.OIDC.TrustForwardedURI = true
return cfg, nil return cfg, nil
@@ -152,8 +156,6 @@ func setScalarField(cfg *Config, field, value string) {
case "CACertPEM": case "CACertPEM":
cfg.OIDC.CACertPEM = value cfg.OIDC.CACertPEM = value
} }
// strconv reserved for future int/bool fields when added to the allow-list.
_ = strconv.Atoi
} }
func applyDefaults(cfg *Config) { func applyDefaults(cfg *Config) {
@@ -170,22 +172,20 @@ func applyDefaults(cfg *Config) {
// Multi-letter acronyms keep their grouping: "OIDCEndSessionURL" → // Multi-letter acronyms keep their grouping: "OIDCEndSessionURL" →
// "OIDC_END_SESSION_URL", "CACertPEM" → "CA_CERT_PEM". // "OIDC_END_SESSION_URL", "CACertPEM" → "CA_CERT_PEM".
func camelToSnakeUpper(s string) string { func camelToSnakeUpper(s string) string {
runes := []rune(s)
var b strings.Builder var b strings.Builder
for i, r := range s { for i, r := range runes {
if i > 0 && isUpper(r) { if i > 0 && isUpper(r) {
prev := rune(s[i-1]) prev := runes[i-1]
next := rune(0) next := rune(0)
if i+1 < len(s) { if i+1 < len(runes) {
next = rune(s[i+1]) next = runes[i+1]
} }
if !isUpper(prev) || (next != 0 && !isUpper(next)) { if !isUpper(prev) || (next != 0 && !isUpper(next)) {
b.WriteByte('_') b.WriteByte('_')
} }
} }
if r >= 'a' && r <= 'z' { b.WriteRune(unicode.ToUpper(r))
r -= 32
}
b.WriteRune(r)
} }
return b.String() return b.String()
} }
+29
View File
@@ -106,3 +106,32 @@ func TestLoad_MissingFile(t *testing.T) {
t.Fatal("expected error for missing file") 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)
}
}