Add option to read the password from a file

This commit is contained in:
Alexander Neumann 2016-09-12 14:08:51 +02:00
parent 791f73e0db
commit 65afeba19a
4 changed files with 59 additions and 25 deletions

View file

@ -20,9 +20,12 @@ func (cmd CmdInit) Execute(args []string) error {
} }
if cmd.global.password == "" { if cmd.global.password == "" {
cmd.global.password = cmd.global.ReadPasswordTwice( cmd.global.password, err = cmd.global.ReadPasswordTwice(
"enter password for new backend: ", "enter password for new backend: ",
"enter password again: ") "enter password again: ")
if err != nil {
return err
}
} }
s := repository.New(be) s := repository.New(be)

View file

@ -56,9 +56,9 @@ func (cmd CmdKey) listKeys(s *repository.Repository) error {
return tab.Write(cmd.global.stdout) return tab.Write(cmd.global.stdout)
} }
func (cmd CmdKey) getNewPassword() string { func (cmd CmdKey) getNewPassword() (string, error) {
if cmd.newPassword != "" { if cmd.newPassword != "" {
return cmd.newPassword return cmd.newPassword, nil
} }
return cmd.global.ReadPasswordTwice( return cmd.global.ReadPasswordTwice(
@ -67,7 +67,12 @@ func (cmd CmdKey) getNewPassword() string {
} }
func (cmd CmdKey) addKey(repo *repository.Repository) error { func (cmd CmdKey) addKey(repo *repository.Repository) error {
id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key()) pw, err := cmd.getNewPassword()
if err != nil {
return err
}
id, err := repository.AddKey(repo, pw, repo.Key())
if err != nil { if err != nil {
return errors.Fatalf("creating new key failed: %v\n", err) return errors.Fatalf("creating new key failed: %v\n", err)
} }
@ -92,7 +97,12 @@ func (cmd CmdKey) deleteKey(repo *repository.Repository, name string) error {
} }
func (cmd CmdKey) changePassword(repo *repository.Repository) error { func (cmd CmdKey) changePassword(repo *repository.Repository) error {
id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key()) pw, err := cmd.getNewPassword()
if err != nil {
return err
}
id, err := repository.AddKey(repo, pw, repo.Key())
if err != nil { if err != nil {
return errors.Fatalf("creating new key failed: %v\n", err) return errors.Fatalf("creating new key failed: %v\n", err)
} }

View file

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"restic" "restic"
"runtime" "runtime"
@ -28,11 +29,12 @@ var compiledAt = "unknown time"
// GlobalOptions holds all those options that can be set for every command. // GlobalOptions holds all those options that can be set for every command.
type GlobalOptions struct { type GlobalOptions struct {
Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restore from"` Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restore from"`
CacheDir string ` long:"cache-dir" description:"Directory to use as a local cache"` PasswordFile string `short:"p" long:"password-file" description:"Read the repository password from a file"`
Quiet bool `short:"q" long:"quiet" default:"false" description:"Do not output comprehensive progress report"` CacheDir string ` long:"cache-dir" description:"Directory to use as a local cache"`
NoLock bool ` long:"no-lock" default:"false" description:"Do not lock the repo, this allows some operations on read-only repos."` Quiet bool `short:"q" long:"quiet" default:"false" description:"Do not output comprehensive progress report"`
Options []string `short:"o" long:"option" description:"Specify options in the form 'foo.key=value'"` NoLock bool ` long:"no-lock" default:"false" description:"Do not lock the repo, this allows some operations on read-only repos."`
Options []string `short:"o" long:"option" description:"Specify options in the form 'foo.key=value'"`
password string password string
stdout io.Writer stdout io.Writer
@ -185,7 +187,7 @@ func readPassword(in io.Reader) (password string, err error) {
buf = buf[:n] buf = buf[:n]
if err != nil && errors.Cause(err) != io.ErrUnexpectedEOF { if err != nil && errors.Cause(err) != io.ErrUnexpectedEOF {
return "", err return "", errors.Wrap(err, "ReadFull")
} }
return strings.TrimRight(string(buf), "\r\n"), nil return strings.TrimRight(string(buf), "\r\n"), nil
@ -199,15 +201,25 @@ func readPasswordTerminal(in *os.File, out io.Writer, prompt string) (password s
buf, err := terminal.ReadPassword(int(in.Fd())) buf, err := terminal.ReadPassword(int(in.Fd()))
fmt.Fprintln(out) fmt.Fprintln(out)
if err != nil { if err != nil {
return "", err return "", errors.Wrap(err, "ReadPassword")
} }
password = string(buf) password = string(buf)
return password, nil return password, nil
} }
// ReadPassword reads the password from stdin. // ReadPassword reads the password from a password file, the environment
func (o GlobalOptions) ReadPassword(prompt string) string { // variable RESTIC_PASSWORD or prompts the user.
func (o GlobalOptions) ReadPassword(prompt string) (string, error) {
if o.PasswordFile != "" {
s, err := ioutil.ReadFile(o.PasswordFile)
return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile")
}
if pwd := os.Getenv("RESTIC_PASSWORD"); pwd != "" {
return pwd, nil
}
var ( var (
password string password string
err error err error
@ -220,26 +232,33 @@ func (o GlobalOptions) ReadPassword(prompt string) string {
} }
if err != nil { if err != nil {
o.Exitf(2, "unable to read password: %v", err) return "", errors.Wrap(err, "unable to read password")
} }
if len(password) == 0 { if len(password) == 0 {
o.Exitf(1, "an empty password is not a password") return "", errors.Fatal("an empty password is not a password")
} }
return password return password, nil
} }
// ReadPasswordTwice calls ReadPassword two times and returns an error when the // ReadPasswordTwice calls ReadPassword two times and returns an error when the
// passwords don't match. // passwords don't match.
func (o GlobalOptions) ReadPasswordTwice(prompt1, prompt2 string) string { func (o GlobalOptions) ReadPasswordTwice(prompt1, prompt2 string) (string, error) {
pw1 := o.ReadPassword(prompt1) pw1, err := o.ReadPassword(prompt1)
pw2 := o.ReadPassword(prompt2) if err != nil {
if pw1 != pw2 { return "", err
o.Exitf(1, "passwords do not match") }
pw2, err := o.ReadPassword(prompt2)
if err != nil {
return "", err
} }
return pw1 if pw1 != pw2 {
return "", errors.Fatal("passwords do not match")
}
return pw1, nil
} }
const maxKeys = 20 const maxKeys = 20
@ -258,7 +277,10 @@ func (o GlobalOptions) OpenRepository() (*repository.Repository, error) {
s := repository.New(be) s := repository.New(be)
if o.password == "" { if o.password == "" {
o.password = o.ReadPassword("enter password for repository: ") o.password, err = o.ReadPassword("enter password for repository: ")
if err != nil {
return nil, err
}
} }
err = s.SearchKey(o.password, maxKeys) err = s.SearchKey(o.password, maxKeys)

View file

@ -28,7 +28,6 @@ func main() {
// defer profile.Start(profile.MemProfileRate(100000), profile.ProfilePath(".")).Stop() // defer profile.Start(profile.MemProfileRate(100000), profile.ProfilePath(".")).Stop()
// defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop() // defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop()
globalOpts.Repo = os.Getenv("RESTIC_REPOSITORY") globalOpts.Repo = os.Getenv("RESTIC_REPOSITORY")
globalOpts.password = os.Getenv("RESTIC_PASSWORD")
debug.Log("restic", "main %#v", os.Args) debug.Log("restic", "main %#v", os.Args)