Implement loading passwords from external commands

* Resolves #80
This commit is contained in:
Galen Abell 2019-05-18 15:29:26 -04:00 committed by Drew DeVault
parent 98da4c9509
commit b8208509f4
4 changed files with 94 additions and 8 deletions

View file

@ -3,7 +3,9 @@ package config
import ( import (
"errors" "errors"
"fmt" "fmt"
"net/url"
"os" "os"
"os/exec"
"path" "path"
"regexp" "regexp"
"strings" "strings"
@ -29,14 +31,16 @@ const (
) )
type AccountConfig struct { type AccountConfig struct {
CopyTo string CopyTo string
Default string Default string
From string From string
Name string Name string
Source string Source string
Folders []string SourceCredCmd string
Params map[string]string Folders []string
Outgoing string Params map[string]string
Outgoing string
OutgoingCredCmd string
} }
type BindingConfig struct { type BindingConfig struct {
@ -115,8 +119,12 @@ func loadAccountConfig(path string) ([]AccountConfig, error) {
for key, val := range sec.KeysHash() { for key, val := range sec.KeysHash() {
if key == "folders" { if key == "folders" {
account.Folders = strings.Split(val, ",") account.Folders = strings.Split(val, ",")
} else if key == "source_cred_cmd" {
account.SourceCredCmd = val
} else if key == "outgoing" { } else if key == "outgoing" {
account.Outgoing = val account.Outgoing = val
} else if key == "outgoing_cred_cmd" {
account.OutgoingCredCmd = val
} else if key == "from" { } else if key == "from" {
account.From = val account.From = val
} else if key == "copy-to" { } else if key == "copy-to" {
@ -128,6 +136,19 @@ func loadAccountConfig(path string) ([]AccountConfig, error) {
if account.Source == "" { if account.Source == "" {
return nil, fmt.Errorf("Expected source for account %s", _sec) return nil, fmt.Errorf("Expected source for account %s", _sec)
} }
source, err := parseCredential(account.Source, account.SourceCredCmd)
if err != nil {
return nil, fmt.Errorf("Invalid source credentials for %s: %s", _sec, err)
}
account.Source = source
outgoing, err := parseCredential(account.Outgoing, account.OutgoingCredCmd)
if err != nil {
return nil, fmt.Errorf("Invalid outgoing credentials for %s: %s", _sec, err)
}
account.Outgoing = outgoing
accounts = append(accounts, account) accounts = append(accounts, account)
} }
if len(accounts) == 0 { if len(accounts) == 0 {
@ -137,6 +158,38 @@ func loadAccountConfig(path string) ([]AccountConfig, error) {
return accounts, nil return accounts, nil
} }
func parseCredential(cred, command string) (string, error) {
if cred == "" || command == "" {
return cred, nil
}
u, err := url.Parse(cred)
if err != nil {
return "", err
}
// ignore the command if a password is specified
if _, exists := u.User.Password(); exists {
return cred, nil
}
// don't attempt to parse the command if the url is a path (ie /usr/bin/sendmail)
if !u.IsAbs() {
return cred, nil
}
cmd := exec.Command("sh", "-c", command)
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to read password: %s", err)
}
pw := strings.TrimSpace(string(output))
u.User = url.UserPassword(u.User.Username(), pw)
return u.String(), nil
}
func LoadConfig(root *string) (*AercConfig, error) { func LoadConfig(root *string) (*AercConfig, error) {
if root == nil { if root == nil {
_root := path.Join(xdg.ConfigHome(), "aerc") _root := path.Join(xdg.ConfigHome(), "aerc")

View file

@ -144,6 +144,12 @@ Note that many of these configuration options are written for you, such as
- *aerc-smtp*(5) - *aerc-smtp*(5)
*outgoing_cred_cmd*
Specifies an optional command that is run to get the outgoing account's
password. See each protocol's man page for more details:
- *aerc-smtp*(5)
*source* *source*
Specifies the source for reading incoming emails on this account. This key Specifies the source for reading incoming emails on this account. This key
is required for all accounts. It should be a connection string, and the is required for all accounts. It should be a connection string, and the
@ -154,6 +160,13 @@ Note that many of these configuration options are written for you, such as
Default: none Default: none
*source_cred_cmd*
Specifies an optional command that is run to get the source account's
password. See each protocol's man page for more details:
- *aerc-imap*(5)
# BINDS.CONF # BINDS.CONF
This file is used for configuring keybindings used in the aerc interactive This file is used for configuring keybindings used in the aerc interactive

View file

@ -35,6 +35,16 @@ available:
*imaps*: *imaps*:
IMAP with TLS/SSL IMAP with TLS/SSL
*source_cred_cmd*
Specifies the command to run to get the password for the IMAP
account. This command will be run using `sh -c [command]`. If a
password is specified in the *source* option, the password will
take precedence over this command.
Example:
`pass hostname/username`
# SEE ALSO # SEE ALSO
*aerc*(1) *aerc-config*(5) *aerc-smtp*(5) *aerc*(1) *aerc-config*(5) *aerc-smtp*(5)

View file

@ -39,6 +39,16 @@ available:
Authenticate with a username and password using AUTH PLAIN. This is the Authenticate with a username and password using AUTH PLAIN. This is the
default behavior. default behavior.
*outgoing_cred_cmd*
Specifies the command to run to get the password for the SMTP
account. This command will be run using `sh -c [command]`. If a
password is specified in the *outgoing* option, the password will
take precedence over this command.
Example:
`pass hostname/username`
# SEE ALSO # SEE ALSO
*aerc*(1) *aerc-config*(5) *aerc-smtp*(5) *aerc*(1) *aerc-config*(5) *aerc-smtp*(5)