fix overriding default user with environment variables (#5371)

MTX_AUTHINTERNALUSERS_0_USER and MTX_AUTHINTERNALUSERS_0_PASS are now working even when the configuration file is present.
This commit is contained in:
Alessandro Ros 2026-01-24 19:38:59 +01:00 committed by GitHub
parent f4f795a2a6
commit 0d95459f7b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 96 additions and 58 deletions

View file

@ -219,17 +219,28 @@ func loadEnvInternal(env map[string]string, prefix string, prv reflect.Value) er
} else {
for i := 0; ; i++ {
itemPrefix := prefix + "_" + strconv.FormatInt(int64(i), 10)
if !envHasAtLeastAKeyWithPrefix(env, itemPrefix) {
if !envHasAtLeastAKeyWithPrefix(env, itemPrefix) && (prv.IsZero() || prv.Elem().Len() <= i) {
break
}
elem := reflect.New(rt.Elem())
var elem reflect.Value
if prv.Elem().Len() > i {
elem = prv.Elem().Index(i).Addr()
} else {
elem = reflect.New(rt.Elem())
}
err := loadEnvInternal(env, itemPrefix, elem.Elem())
if err != nil {
return err
}
prv.Elem().Set(reflect.Append(prv.Elem(), elem.Elem()))
if prv.Elem().Len() > i {
prv.Elem().Index(i).Set(elem.Elem())
} else {
prv.Elem().Set(reflect.Append(prv.Elem(), elem.Elem()))
}
}
}
return nil

View file

@ -51,67 +51,79 @@ type mySubStruct struct {
}
type testStruct struct {
MyString string `json:"myString"`
MyStringOpt *string `json:"myStringOpt"`
MyInt int `json:"myInt"`
MyIntOpt *int `json:"myIntOpt"`
MyUint uint `json:"myUint"`
MyUintOpt *uint `json:"myUintOpt"`
MyFloat float64 `json:"myFloat"`
MyFloatOpt *float64 `json:"myFloatOpt"`
MyBool bool `json:"myBool"`
MyBoolOpt *bool `json:"myBoolOpt"`
MyDuration myDuration `json:"myDuration"`
MyDurationOpt *myDuration `json:"myDurationOpt"`
MyDurationOptUnset *myDuration `json:"myDurationOptUnset"`
MyMap map[string]*mapEntry `json:"myMap"`
MySliceFloat []float64 `json:"mySliceFloat"`
MySliceString []string `json:"mySliceString"`
MySliceStringEmpty []string `json:"mySliceStringEmpty"`
MySliceStringOpt *[]string `json:"mySliceStringOpt"`
MySliceStringOptUnset *[]string `json:"mySliceStringOptUnset"`
MySliceSubStruct []mySubStruct `json:"mySliceSubStruct"`
MySliceSubStructEmpty []mySubStruct `json:"mySliceSubStructEmpty"`
MySliceSubStructOpt *[]mySubStruct `json:"mySliceSubStructOpt"`
MySliceSubStructOptUnset *[]mySubStruct `json:"mySliceSubStructOptUnset"`
Unset *bool `json:"unset"`
MyString string `json:"myString"`
MyStringOpt *string `json:"myStringOpt"`
MyInt int `json:"myInt"`
MyIntOpt *int `json:"myIntOpt"`
MyUint uint `json:"myUint"`
MyUintOpt *uint `json:"myUintOpt"`
MyFloat float64 `json:"myFloat"`
MyFloatOpt *float64 `json:"myFloatOpt"`
MyBool bool `json:"myBool"`
MyBoolOpt *bool `json:"myBoolOpt"`
MyDuration myDuration `json:"myDuration"`
MyDurationOpt *myDuration `json:"myDurationOpt"`
MyDurationOptUnset *myDuration `json:"myDurationOptUnset"`
MyMap map[string]*mapEntry `json:"myMap"`
MySliceFloat []float64 `json:"mySliceFloat"`
MySliceString []string `json:"mySliceString"`
MySliceStringEmpty []string `json:"mySliceStringEmpty"`
MySliceStringOpt *[]string `json:"mySliceStringOpt"`
MySliceStringOptUnset *[]string `json:"mySliceStringOptUnset"`
MySliceSubStruct []mySubStruct `json:"mySliceSubStruct"`
MySliceSubStructEmpty []mySubStruct `json:"mySliceSubStructEmpty"`
MySliceSubStructOpt *[]mySubStruct `json:"mySliceSubStructOpt"`
MySliceSubStructOptUnset *[]mySubStruct `json:"mySliceSubStructOptUnset"`
MySliceSubStructPreloaded []mySubStruct `json:"mySliceSubStructPreloaded"`
MySliceSubStructPreloaded2 []mySubStruct `json:"mySliceSubStructPreloaded2"`
Unset *bool `json:"unset"`
}
func TestLoad(t *testing.T) {
env := map[string]string{
"MYPREFIX_MYSTRING": "testcontent",
"MYPREFIX_MYSTRINGOPT": "testcontent2",
"MYPREFIX_MYINT": "123",
"MYPREFIX_MYINTOPT": "456",
"MYPREFIX_MYUINT": "8910",
"MYPREFIX_MYUINTOPT": "112313",
"MYPREFIX_MYFLOAT": "15.2",
"MYPREFIX_MYFLOATOPT": "16.2",
"MYPREFIX_MYBOOL": "yes",
"MYPREFIX_MYBOOLOPT": "false",
"MYPREFIX_MYDURATION": "22s",
"MYPREFIX_MYDURATIONOPT": "30s",
"MYPREFIX_MYMAP_MYKEY": "",
"MYPREFIX_MYMAP_MYKEY2_MYVALUE": "asd",
"MYPREFIX_MYMAP_MYKEY2_MYSTRUCT_MYPARAM": "456",
"MYPREFIX_MYSLICEFLOAT": "0.5,0.5",
"MYPREFIX_MYSLICESTRING": "val1,val2",
"MYPREFIX_MYSLICESTRINGEMPTY": "",
"MYPREFIX_MYSLICESTRINGOPT": "aa",
"MYPREFIX_MYSLICESUBSTRUCT_0_URL": "url1",
"MYPREFIX_MYSLICESUBSTRUCT_0_USERNAME": "user1",
"MYPREFIX_MYSLICESUBSTRUCT_0_PASSWORD": "pass1",
"MYPREFIX_MYSLICESUBSTRUCT_1_URL": "url2",
"MYPREFIX_MYSLICESUBSTRUCT_1_PASSWORD": "pass2",
"MYPREFIX_MYSLICESUBSTRUCTEMPTY": "",
"MYPREFIX_MYSLICESUBSTRUCTOPT_1_PASSWORD": "pwd",
s := testStruct{
MySliceSubStructPreloaded: []mySubStruct{
{
URL: "val1",
Username: "val2",
},
},
MySliceSubStructPreloaded2: []mySubStruct{
{
URL: "val3",
Username: "val4",
},
},
}
for key, val := range env {
t.Setenv(key, val)
}
t.Setenv("MYPREFIX_MYSTRING", "testcontent")
t.Setenv("MYPREFIX_MYSTRINGOPT", "testcontent2")
t.Setenv("MYPREFIX_MYINT", "123")
t.Setenv("MYPREFIX_MYINTOPT", "456")
t.Setenv("MYPREFIX_MYUINT", "8910")
t.Setenv("MYPREFIX_MYUINTOPT", "112313")
t.Setenv("MYPREFIX_MYFLOAT", "15.2")
t.Setenv("MYPREFIX_MYFLOATOPT", "16.2")
t.Setenv("MYPREFIX_MYBOOL", "yes")
t.Setenv("MYPREFIX_MYBOOLOPT", "false")
t.Setenv("MYPREFIX_MYDURATION", "22s")
t.Setenv("MYPREFIX_MYDURATIONOPT", "30s")
t.Setenv("MYPREFIX_MYMAP_MYKEY", "")
t.Setenv("MYPREFIX_MYMAP_MYKEY2_MYVALUE", "asd")
t.Setenv("MYPREFIX_MYMAP_MYKEY2_MYSTRUCT_MYPARAM", "456")
t.Setenv("MYPREFIX_MYSLICEFLOAT", "0.5,0.5")
t.Setenv("MYPREFIX_MYSLICESTRING", "val1,val2")
t.Setenv("MYPREFIX_MYSLICESTRINGEMPTY", "")
t.Setenv("MYPREFIX_MYSLICESTRINGOPT", "aa")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCT_0_URL", "url1")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCT_0_USERNAME", "user1")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCT_0_PASSWORD", "pass1")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCT_1_URL", "url2")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCT_1_PASSWORD", "pass2")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCTEMPTY", "")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCTOPT_1_PASSWORD", "pwd")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCTPRELOADED_0_URL", "newurl")
t.Setenv("MYPREFIX_MYSLICESUBSTRUCTPRELOADED2_1_URL", "newurl2")
var s testStruct
err := Load("MYPREFIX", &s)
require.NoError(t, err)
@ -162,6 +174,21 @@ func TestLoad(t *testing.T) {
},
},
MySliceSubStructEmpty: []mySubStruct{},
MySliceSubStructPreloaded: []mySubStruct{
{
URL: "newurl",
Username: "val2",
},
},
MySliceSubStructPreloaded2: []mySubStruct{
{
URL: "val3",
Username: "val4",
},
{
URL: "newurl2",
},
},
}, s)
}