fix(auth): tolerate string booleans in oidc provider config (#2599)

The four boolean OIDC provider fields (emailfallback, usernamefallback,
forceuserinfo, requireavailability) were parsed with a strict .(bool)
type assertion. That works for YAML/JSON config where leaves are native
bools, but fails for every other input path: env vars always arrive as
strings, and GetConfigValueFromFile (used by the *.file Docker secret
convention) also always returns strings. The assertion would silently
zero the field for emailfallback and usernamefallback, and log an error
and zero the field for forceuserinfo and requireavailability, which is
what #2599 reports.

Extract a small parseBoolField helper that accepts both native bools and
strings (via strconv.ParseBool) and logs a parse error from each call
site. This also fixes the previously-silent drop of stringified
emailfallback / usernamefallback values — those now log an error if the
input is garbage, matching the behaviour of the other two fields.

Fixes #2599
This commit is contained in:
kolaente 2026-04-11 21:00:16 +02:00 committed by kolaente
parent 3008dc09db
commit ced7ebd97f
1 changed files with 34 additions and 34 deletions

View File

@ -180,6 +180,28 @@ func GetProvider(key string) (provider *Provider, err error) {
return
}
// parseBoolField reads a boolean-valued config field from a provider map,
// tolerating both native bools (from YAML/JSON) and strings (from env vars or
// the GetConfigValueFromFile path, which always return strings). Missing or
// empty values default to false with no error.
func parseBoolField(pi map[string]interface{}, key string) (val bool, err error) {
raw, exists := pi[key]
if !exists {
return false, nil
}
switch v := raw.(type) {
case bool:
return v, nil
case string:
if v == "" {
return false, nil
}
return strconv.ParseBool(v)
default:
return false, fmt.Errorf("expected bool, got %T", raw)
}
}
func getProviderFromMap(pi map[string]interface{}, key string) (provider *Provider, err error) {
requiredKeys := []string{
@ -236,43 +258,21 @@ func getProviderFromMap(pi map[string]interface{}, key string) (provider *Provid
scope = "openid profile email"
}
var emailFallback = false
emailFallbackValue, exists := pi["emailfallback"]
if exists {
emailFallbackTypedValue, ok := emailFallbackValue.(bool)
if ok {
emailFallback = emailFallbackTypedValue
}
emailFallback, err := parseBoolField(pi, "emailfallback")
if err != nil {
log.Errorf("emailfallback is not a boolean for provider %s: %s", key, err)
}
var usernameFallback = false
usernameFallbackValue, exists := pi["usernamefallback"]
if exists {
usernameFallbackTypedValue, ok := usernameFallbackValue.(bool)
if ok {
usernameFallback = usernameFallbackTypedValue
}
usernameFallback, err := parseBoolField(pi, "usernamefallback")
if err != nil {
log.Errorf("usernamefallback is not a boolean for provider %s: %s", key, err)
}
var forceUserInfo = false
forceUserInfoValue, exists := pi["forceuserinfo"]
if exists {
forceUserInfoTypedValue, ok := forceUserInfoValue.(bool)
if ok {
forceUserInfo = forceUserInfoTypedValue
} else {
log.Errorf("forceuserinfo is not a boolean for provider %s, value: %v", key, forceUserInfoValue)
}
forceUserInfo, err := parseBoolField(pi, "forceuserinfo")
if err != nil {
log.Errorf("forceuserinfo is not a boolean for provider %s: %s", key, err)
}
var requireAvailability = false
requireAvailabilityValue, exists := pi["requireavailability"]
if exists {
requireAvailabilityTypedValue, ok := requireAvailabilityValue.(bool)
if ok {
requireAvailability = requireAvailabilityTypedValue
} else {
log.Errorf("requireavailability is not a boolean for provider %s, value: %v", key, requireAvailabilityValue)
}
requireAvailability, err := parseBoolField(pi, "requireavailability")
if err != nil {
log.Errorf("requireavailability is not a boolean for provider %s: %s", key, err)
}
provider = &Provider{