diff --git a/cli/cli.go b/cli/cli.go index 56a9a38..b6098ac 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -122,7 +122,7 @@ func New(cfg *config.XCConfig, backend store.Backend) (*Cli, error) { if cfg.PasswordManagerPath != "" { term.Warnf("Loading password manager from %s\n", cfg.PasswordManagerPath) - err = passmgr.Load(cfg.PasswordManagerPath) + err = passmgr.Load(cfg.PasswordManagerPath, cfg.PasswordManagerOptions) if err != nil { term.Errorf("Error initializing password manager: %s\n", err) } else { @@ -482,6 +482,5 @@ func (c *Cli) setDistributeType(dtr string) { term.Errorf("Unknown distribute type: %s\n", dtr) return } - term.Successf("distribute_type set to %s\n", dtr) remote.SetDistributeType(c.distributeType) } diff --git a/cli/handlers.go b/cli/handlers.go index 4dc6cad..5b81492 100644 --- a/cli/handlers.go +++ b/cli/handlers.go @@ -159,6 +159,7 @@ func (c *Cli) doDistributeType(name string, argsLine string, args ...string) { return } c.setDistributeType(args[0]) + term.Successf("distribute_type set to %s\n", args[0]) } func (c *Cli) doPasswd(name string, argsLine string, args ...string) { diff --git a/cli/help.go b/cli/help.go index 4340e77..c8de278 100644 --- a/cli/help.go +++ b/cli/help.go @@ -164,8 +164,15 @@ Rcfile is just a number of xc commands in a text file.`, "passmgr": &helpItem{ isTopic: true, help: `Password manager is a golang plugin which must have two exported functions: -func Init() error, which is called on xc start -func GetPass(host string) (password string), which is kinda self-explanatory + +func Init(options map[string]string, debugf func(string, ...interface{})) error +func GetPass(host string) string + +Init function is called on xc start passing all the options found in [passmgr] config section +as a map[string]string, and a debugf function which logs to xc shared log if it's enabled. + +GetPass function is called to acquire proper password for a host. Xc passes a hostname as the +only argument and expects function to return a password. For more info on how to write golang plugins, please refer to golang documentation or this article: https://medium.com/learning-the-go-programming-language/writing-modular-go-programs-with-plugins-ec46381ee1a9`, diff --git a/config/config.go b/config/config.go index fc9c126..eeff64f 100644 --- a/config/config.go +++ b/config/config.go @@ -65,31 +65,32 @@ type BackendConfig struct { // XCConfig represents a configuration struct for XC type XCConfig struct { - Readline *readline.Config - BackendCfg *BackendConfig - User string - SSHThreads int - SSHConnectTimeout int - PingCount int - RemoteTmpdir string - Mode string - RaiseType string - Delay int - RCfile string - CacheDir string - CacheTTL time.Duration - Debug bool - ProgressBar bool - PrependHostnames bool - LogFile string - ExitConfirm bool - ExecConfirm bool - SudoInterpreter string - SuInterpreter string - Interpreter string - PasswordManagerPath string - LocalEnvironment map[string]string - Distribute string + Readline *readline.Config + BackendCfg *BackendConfig + User string + SSHThreads int + SSHConnectTimeout int + PingCount int + RemoteTmpdir string + Mode string + RaiseType string + Delay int + RCfile string + CacheDir string + CacheTTL time.Duration + Debug bool + ProgressBar bool + PrependHostnames bool + LogFile string + ExitConfirm bool + ExecConfirm bool + SudoInterpreter string + SuInterpreter string + Interpreter string + PasswordManagerPath string + PasswordManagerOptions map[string]string + LocalEnvironment map[string]string + Distribute string } const ( @@ -325,7 +326,19 @@ func read(filename string, secondPass bool) (*XCConfig, error) { return nil, fmt.Errorf("Error configuring backend: backend type is not defined") } - cfg.PasswordManagerPath, _ = props.GetString("passmgr.path") + cfg.PasswordManagerOptions = make(map[string]string) + if props.KeyExists("passmgr") { + pmgrkeys, err := props.Subkeys("passmgr") + if err == nil { + for _, key := range pmgrkeys { + if key == "path" { + cfg.PasswordManagerPath, _ = props.GetString("passmgr.path") + } else { + cfg.PasswordManagerOptions[key], _ = props.GetString(fmt.Sprintf("passmgr.%s", key)) + } + } + } + } envkeys, err := props.Subkeys("environ") if err == nil { diff --git a/passmgr/passmgr.go b/passmgr/passmgr.go index 6ce9a02..e7092ec 100644 --- a/passmgr/passmgr.go +++ b/passmgr/passmgr.go @@ -3,10 +3,9 @@ package passmgr import ( "fmt" "plugin" -) -type initFunc func() error -type acquireFunc func(string) string + "github.com/viert/xc/log" +) const ( initFuncName = "Init" @@ -16,12 +15,12 @@ const ( var ( p *plugin.Plugin initialized bool - pluginInit initFunc - pluginAcquire acquireFunc + pluginInit func(map[string]string, func(string, ...interface{})) error + pluginAcquire func(string) string ) // Load loads a password manager library -func Load(filename string) error { +func Load(filename string, options map[string]string) error { var err error p, err = plugin.Open(filename) if err != nil { @@ -32,7 +31,7 @@ func Load(filename string) error { if err != nil { return err } - pluginInit, initialized = init.(initFunc) + pluginInit, initialized = init.(func(map[string]string, func(string, ...interface{})) error) if !initialized { return fmt.Errorf("invalid plugin `%s() error` function signature", initFuncName) } @@ -41,11 +40,15 @@ func Load(filename string) error { if err != nil { return err } - pluginAcquire, initialized = acq.(acquireFunc) + pluginAcquire, initialized = acq.(func(string) string) if !initialized { return fmt.Errorf("invalid plugin `%s(string) string` function signature", acquireFuncName) } - return nil + + err = pluginInit(options, log.Debugf) + initialized = err == nil + + return err } // GetPass returns password for a given host from password manager