config: factor --password-command code into its own function #7859

This commit is contained in:
Nick Craig-Wood 2024-07-23 12:36:04 +01:00
parent 71799d7efd
commit c9c283533c
2 changed files with 69 additions and 27 deletions

View File

@ -78,37 +78,19 @@ func Decrypt(b io.ReadSeeker) (io.Reader, error) {
} }
if len(configKey) == 0 { if len(configKey) == 0 {
if len(ci.PasswordCommand) != 0 { pass, err := GetPasswordCommand(ctx)
var stdout bytes.Buffer if err != nil {
var stderr bytes.Buffer return nil, err
}
cmd := exec.Command(ci.PasswordCommand[0], ci.PasswordCommand[1:]...) if pass != "" {
usingPasswordCommand = true
cmd.Stdout = &stdout err = SetConfigPassword(pass)
cmd.Stderr = &stderr if err != nil {
cmd.Stdin = os.Stdin return nil, fmt.Errorf("incorrect password: %w", err)
if err := cmd.Run(); err != nil {
// One does not always get the stderr returned in the wrapped error.
fs.Errorf(nil, "Using --password-command returned: %v", err)
if ers := strings.TrimSpace(stderr.String()); ers != "" {
fs.Errorf(nil, "--password-command stderr: %s", ers)
}
return nil, fmt.Errorf("password command failed: %w", err)
} }
if pass := strings.Trim(stdout.String(), "\r\n"); pass != "" {
err := SetConfigPassword(pass)
if err != nil {
return nil, fmt.Errorf("incorrect password: %w", err)
}
} else {
return nil, errors.New("password-command returned empty string")
}
if len(configKey) == 0 { if len(configKey) == 0 {
return nil, errors.New("unable to decrypt configuration: incorrect password") return nil, errors.New("unable to decrypt configuration: incorrect password")
} }
usingPasswordCommand = true
} else { } else {
usingPasswordCommand = false usingPasswordCommand = false
@ -183,6 +165,40 @@ func Decrypt(b io.ReadSeeker) (io.Reader, error) {
return bytes.NewReader(out), nil return bytes.NewReader(out), nil
} }
// GetPasswordCommand gets the password using the --password-command setting
//
// If the the --password-command flag was not in use it returns "", nil
func GetPasswordCommand(ctx context.Context) (pass string, err error) {
ci := fs.GetConfig(ctx)
if len(ci.PasswordCommand) == 0 {
return "", nil
}
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd := exec.Command(ci.PasswordCommand[0], ci.PasswordCommand[1:]...)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd.Stdin = os.Stdin
err = cmd.Run()
if err != nil {
// One does not always get the stderr returned in the wrapped error.
fs.Errorf(nil, "Using --password-command returned: %v", err)
if ers := strings.TrimSpace(stderr.String()); ers != "" {
fs.Errorf(nil, "--password-command stderr: %s", ers)
}
return pass, fmt.Errorf("password command failed: %w", err)
}
pass = strings.Trim(stdout.String(), "\r\n")
if pass == "" {
return pass, errors.New("--password-command returned empty string")
}
return pass, nil
}
// Encrypt the config file // Encrypt the config file
func Encrypt(src io.Reader, dst io.Writer) error { func Encrypt(src io.Reader, dst io.Writer) error {
if len(configKey) == 0 { if len(configKey) == 0 {

View File

@ -113,3 +113,29 @@ func TestConfigLoadEncryptedFailures(t *testing.T) {
err = config.Data().Load() err = config.Data().Load()
assert.Equal(t, config.ErrorConfigFileNotFound, err) assert.Equal(t, config.ErrorConfigFileNotFound, err)
} }
func TestGetPasswordCommand(t *testing.T) {
ctx, ci := fs.AddConfig(context.Background())
// Not configured
ci.PasswordCommand = fs.SpaceSepList{}
pass, err := config.GetPasswordCommand(ctx)
require.NoError(t, err)
assert.Equal(t, "", pass)
// With password - happy path
ci.PasswordCommand = fs.SpaceSepList{"echo", "asdf"}
pass, err = config.GetPasswordCommand(ctx)
require.NoError(t, err)
assert.Equal(t, "asdf", pass)
// Empty password returned
ci.PasswordCommand = fs.SpaceSepList{"echo", ""}
_, err = config.GetPasswordCommand(ctx)
assert.ErrorContains(t, err, "returned empty string")
// Error when running command
ci.PasswordCommand = fs.SpaceSepList{"XXX non-existent command XXX", ""}
_, err = config.GetPasswordCommand(ctx)
assert.ErrorContains(t, err, "not found")
}