github.com/advanderveer/restic@v0.8.1-0.20171209104529-42a8c19aaea6/cmd/restic/cmd_key.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/restic/restic/internal/errors" 8 "github.com/restic/restic/internal/repository" 9 "github.com/restic/restic/internal/restic" 10 11 "github.com/spf13/cobra" 12 ) 13 14 var cmdKey = &cobra.Command{ 15 Use: "key [list|add|remove|passwd] [ID]", 16 Short: "Manage keys (passwords)", 17 Long: ` 18 The "key" command manages keys (passwords) for accessing the repository. 19 `, 20 DisableAutoGenTag: true, 21 RunE: func(cmd *cobra.Command, args []string) error { 22 return runKey(globalOptions, args) 23 }, 24 } 25 26 func init() { 27 cmdRoot.AddCommand(cmdKey) 28 } 29 30 func listKeys(ctx context.Context, s *repository.Repository) error { 31 tab := NewTable() 32 tab.Header = fmt.Sprintf(" %-10s %-10s %-10s %s", "ID", "User", "Host", "Created") 33 tab.RowFormat = "%s%-10s %-10s %-10s %s" 34 35 for id := range s.List(ctx, restic.KeyFile) { 36 k, err := repository.LoadKey(ctx, s, id.String()) 37 if err != nil { 38 Warnf("LoadKey() failed: %v\n", err) 39 continue 40 } 41 42 var current string 43 if id.String() == s.KeyName() { 44 current = "*" 45 } else { 46 current = " " 47 } 48 tab.Rows = append(tab.Rows, []interface{}{current, id.Str(), 49 k.Username, k.Hostname, k.Created.Format(TimeFormat)}) 50 } 51 52 return tab.Write(globalOptions.stdout) 53 } 54 55 // testKeyNewPassword is used to set a new password during integration testing. 56 var testKeyNewPassword string 57 58 func getNewPassword(gopts GlobalOptions) (string, error) { 59 if testKeyNewPassword != "" { 60 return testKeyNewPassword, nil 61 } 62 63 // Since we already have an open repository, temporary remove the password 64 // to prompt the user for the passwd. 65 newopts := gopts 66 newopts.password = "" 67 68 return ReadPasswordTwice(newopts, 69 "enter password for new key: ", 70 "enter password again: ") 71 } 72 73 func addKey(gopts GlobalOptions, repo *repository.Repository) error { 74 pw, err := getNewPassword(gopts) 75 if err != nil { 76 return err 77 } 78 79 id, err := repository.AddKey(gopts.ctx, repo, pw, repo.Key()) 80 if err != nil { 81 return errors.Fatalf("creating new key failed: %v\n", err) 82 } 83 84 Verbosef("saved new key as %s\n", id) 85 86 return nil 87 } 88 89 func deleteKey(ctx context.Context, repo *repository.Repository, name string) error { 90 if name == repo.KeyName() { 91 return errors.Fatal("refusing to remove key currently used to access repository") 92 } 93 94 h := restic.Handle{Type: restic.KeyFile, Name: name} 95 err := repo.Backend().Remove(ctx, h) 96 if err != nil { 97 return err 98 } 99 100 Verbosef("removed key %v\n", name) 101 return nil 102 } 103 104 func changePassword(gopts GlobalOptions, repo *repository.Repository) error { 105 pw, err := getNewPassword(gopts) 106 if err != nil { 107 return err 108 } 109 110 id, err := repository.AddKey(gopts.ctx, repo, pw, repo.Key()) 111 if err != nil { 112 return errors.Fatalf("creating new key failed: %v\n", err) 113 } 114 115 h := restic.Handle{Type: restic.KeyFile, Name: repo.KeyName()} 116 err = repo.Backend().Remove(gopts.ctx, h) 117 if err != nil { 118 return err 119 } 120 121 Verbosef("saved new key as %s\n", id) 122 123 return nil 124 } 125 126 func runKey(gopts GlobalOptions, args []string) error { 127 if len(args) < 1 || (args[0] == "remove" && len(args) != 2) || (args[0] != "remove" && len(args) != 1) { 128 return errors.Fatal("wrong number of arguments") 129 } 130 131 ctx, cancel := context.WithCancel(gopts.ctx) 132 defer cancel() 133 134 repo, err := OpenRepository(gopts) 135 if err != nil { 136 return err 137 } 138 139 switch args[0] { 140 case "list": 141 lock, err := lockRepo(repo) 142 defer unlockRepo(lock) 143 if err != nil { 144 return err 145 } 146 147 return listKeys(ctx, repo) 148 case "add": 149 lock, err := lockRepo(repo) 150 defer unlockRepo(lock) 151 if err != nil { 152 return err 153 } 154 155 return addKey(gopts, repo) 156 case "remove": 157 lock, err := lockRepoExclusive(repo) 158 defer unlockRepo(lock) 159 if err != nil { 160 return err 161 } 162 163 id, err := restic.Find(repo.Backend(), restic.KeyFile, args[1]) 164 if err != nil { 165 return err 166 } 167 168 return deleteKey(gopts.ctx, repo, id) 169 case "passwd": 170 lock, err := lockRepoExclusive(repo) 171 defer unlockRepo(lock) 172 if err != nil { 173 return err 174 } 175 176 return changePassword(gopts, repo) 177 } 178 179 return nil 180 }