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  }