fix: correctly read user permissions and rules from env

This commit is contained in:
Henrique Dias
2026-10-10 10:26:44 +02:00
parent b7d66d3ab7
commit ee2cd94208
2 changed files with 150 additions and 5 deletions
+26 -5
View File
@@ -142,15 +142,33 @@ func ParseConfig(filename string, flags *pflag.FlagSet) (*Config, error) {
return nil, fmt.Errorf("invalid config: user %q: %w", cfg.Users[i].Username, err)
}
if !v.IsSet(fmt.Sprintf("Users.%d.Permissions", i)) {
// Viper does not apply environment variables to fields of slice elements
// when unmarshaling, so explicitly set values are read back with Get.
userPermissionsKey := fmt.Sprintf("Users.%d.Permissions", i)
if isExplicitlySet(v, flags, userPermissionsKey) {
permissions := Permissions{}
err := permissions.UnmarshalText([]byte(v.GetString(userPermissionsKey)))
if err != nil {
return nil, fmt.Errorf("invalid config: user %q: %w", cfg.Users[i].Username, err)
}
cfg.Users[i].Permissions = permissions
} else {
cfg.Users[i].Permissions = cfg.Permissions
}
if !v.IsSet(fmt.Sprintf("Users.%d.RulesBehavior", i)) {
userRulesBehaviorKey := fmt.Sprintf("Users.%d.RulesBehavior", i)
if isExplicitlySet(v, flags, userRulesBehaviorKey) {
cfg.Users[i].RulesBehavior = RulesBehavior(v.GetString(userRulesBehaviorKey))
} else {
cfg.Users[i].RulesBehavior = cfg.RulesBehavior
}
if v.IsSet(fmt.Sprintf("Users.%d.Rules", i)) {
userRulesKey := fmt.Sprintf("Users.%d.Rules", i)
if os.Getenv(envKey(userRulesKey)) != "" {
return nil, fmt.Errorf("invalid config: user %q: rules cannot be set via environment variables", cfg.Users[i].Username)
}
if isExplicitlySet(v, flags, userRulesKey) {
switch cfg.Users[i].RulesBehavior {
case RulesOverwrite:
// Do nothing
@@ -208,11 +226,14 @@ func isExplicitlySet(v *viper.Viper, flags *pflag.FlagSet, key string) bool {
return true
}
envKey := "WD_" + strings.ToUpper(strings.ReplaceAll(key, ".", "_"))
value, ok := os.LookupEnv(envKey)
value, ok := os.LookupEnv(envKey(key))
return ok && value != ""
}
func envKey(key string) string {
return "WD_" + strings.ToUpper(strings.ReplaceAll(key, ".", "_"))
}
func (c *Config) Validate() error {
var err error
+124
View File
@@ -387,6 +387,130 @@ users:
require.Equal(t, DirectoryMounts{{Name: filepath.Base(userMulti), Path: userMulti}}, cfg.Users[2].Directories)
}
func TestConfigUserEnvOverrides(t *testing.T) {
t.Setenv("WD_USERS_0_PERMISSIONS", "R")
t.Setenv("WD_USERS_1_PERMISSIONS", "none")
t.Setenv("WD_USERS_1_RULESBEHAVIOR", "overwrite")
cfg := writeAndParseConfig(t, `
directory: /
permissions: CRUD
rulesBehavior: append
rules:
- path: /secret
permissions: none
users:
- username: inherited
password: inherited
- username: own
password: own
permissions: CRUD
rules:
- path: /mine`, ".yml")
require.NoError(t, cfg.Validate())
require.Equal(t, Permissions{Read: true}, cfg.Users[0].Permissions)
require.Equal(t, RulesAppend, cfg.Users[0].RulesBehavior)
require.Len(t, cfg.Users[0].Rules, 1)
require.Equal(t, "/secret", cfg.Users[0].Rules[0].Path)
require.Equal(t, Permissions{}, cfg.Users[1].Permissions)
require.Equal(t, RulesOverwrite, cfg.Users[1].RulesBehavior)
require.Len(t, cfg.Users[1].Rules, 1)
require.Equal(t, "/mine", cfg.Users[1].Rules[0].Path)
}
func TestConfigUserRulesEnvRejected(t *testing.T) {
t.Setenv("WD_USERS_0_RULES", "placeholder")
writeAndParseConfigWithError(t, `
directory: /
rules:
- path: /secret
permissions: none
users:
- username: foo
password: bar`, ".yml", "rules cannot be set via environment variables")
}
func TestConfigUserEnvPermissionsNone(t *testing.T) {
t.Setenv("WD_USERS_0_PERMISSIONS", "none")
cfg := writeAndParseConfig(t, `
directory: /
permissions: CRUD
rules:
- path: /secret
permissions: none
users:
- username: foo
password: bar`, ".yml")
require.NoError(t, cfg.Validate())
require.Equal(t, Permissions{}, cfg.Users[0].Permissions)
require.Len(t, cfg.Users[0].Rules, 1)
require.Equal(t, "/secret", cfg.Users[0].Rules[0].Path)
}
func TestConfigUserEnvEmptyInherits(t *testing.T) {
t.Setenv("WD_USERS_0_PERMISSIONS", "")
t.Setenv("WD_USERS_0_RULESBEHAVIOR", "")
t.Setenv("WD_USERS_0_RULES", "")
cfg := writeAndParseConfig(t, `
directory: /
permissions: CRUD
rulesBehavior: append
rules:
- path: /secret
permissions: none
users:
- username: foo
password: bar`, ".yml")
require.NoError(t, cfg.Validate())
require.Equal(t, cfg.Permissions, cfg.Users[0].Permissions)
require.Equal(t, RulesAppend, cfg.Users[0].RulesBehavior)
require.Len(t, cfg.Users[0].Rules, 1)
require.Equal(t, "/secret", cfg.Users[0].Rules[0].Path)
}
func TestConfigUserFileOverrides(t *testing.T) {
t.Parallel()
cfg := writeAndParseConfig(t, `
directory: /
permissions: CRUD
rulesBehavior: append
rules:
- path: /secret
permissions: none
users:
- username: none
password: none
permissions: none
- username: overwrite
password: overwrite
rulesBehavior: overwrite
rules:
- path: /mine
- username: empty
password: empty
rules:`, ".yml")
require.NoError(t, cfg.Validate())
require.Equal(t, Permissions{}, cfg.Users[0].Permissions)
require.Len(t, cfg.Users[0].Rules, 1)
require.Equal(t, RulesOverwrite, cfg.Users[1].RulesBehavior)
require.Len(t, cfg.Users[1].Rules, 1)
require.Equal(t, "/mine", cfg.Users[1].Rules[0].Path)
require.Equal(t, cfg.Permissions, cfg.Users[2].Permissions)
require.Len(t, cfg.Users[2].Rules, 1)
require.Equal(t, "/secret", cfg.Users[2].Rules[0].Path)
}
func TestConfigKeys(t *testing.T) {
t.Parallel()