github.com/GGP1/kure@v0.8.4/commands/add/phrase/phrase.go (about) 1 package phrase 2 3 import ( 4 "bufio" 5 "fmt" 6 "io" 7 "math" 8 "strings" 9 10 "github.com/GGP1/kure/auth" 11 cmdutil "github.com/GGP1/kure/commands" 12 "github.com/GGP1/kure/db/entry" 13 "github.com/GGP1/kure/pb" 14 "github.com/GGP1/kure/terminal" 15 16 "github.com/GGP1/atoll" 17 18 "github.com/pkg/errors" 19 "github.com/spf13/cobra" 20 bolt "go.etcd.io/bbolt" 21 ) 22 23 const example = ` 24 * Add an entry generating a random passphrase 25 kure add phrase Sample -l 6 -s $ -i atoll -e admin,login --list nolist` 26 27 type phraseOptions struct { 28 list, separator string 29 incl, excl []string 30 length uint64 31 } 32 33 // NewCmd returns a new command. 34 func NewCmd(db *bolt.DB, r io.Reader) *cobra.Command { 35 opts := phraseOptions{} 36 cmd := &cobra.Command{ 37 Use: "phrase <name>", 38 Short: "Create an entry using a passphrase", 39 Aliases: []string{"passphrase"}, 40 Example: example, 41 Args: cmdutil.MustNotExist(db, cmdutil.Entry), 42 PreRunE: auth.Login(db), 43 RunE: runPhrase(db, r, &opts), 44 PostRun: func(cmd *cobra.Command, args []string) { 45 // Reset variables (session) 46 opts = phraseOptions{ 47 separator: " ", 48 } 49 }, 50 } 51 52 f := cmd.Flags() 53 f.Uint64VarP(&opts.length, "length", "l", 0, "number of words") 54 f.StringVarP(&opts.separator, "separator", "s", " ", "character that separates each word") 55 f.StringSliceVarP(&opts.incl, "include", "i", nil, "words to include in the passphrase") 56 f.StringSliceVarP(&opts.excl, "exclude", "e", nil, "words to exclude from the passphrase") 57 f.StringVarP(&opts.list, "list", "L", "WordList", "passphrase list used {NoList|WordList|SyllableList}") 58 59 return cmd 60 } 61 62 func runPhrase(db *bolt.DB, r io.Reader, opts *phraseOptions) cmdutil.RunEFunc { 63 return func(cmd *cobra.Command, args []string) error { 64 name := strings.Join(args, " ") 65 name = cmdutil.NormalizeName(name) 66 67 if opts.length < 1 || opts.length > math.MaxUint64 { 68 return cmdutil.ErrInvalidLength 69 } 70 71 e, err := entryInput(r, name) 72 if err != nil { 73 return err 74 } 75 76 e.Password, err = genPassphrase(opts) 77 if err != nil { 78 return err 79 } 80 81 if err := entry.Create(db, e); err != nil { 82 return err 83 } 84 85 fmt.Printf("\n%q added\n", name) 86 return nil 87 } 88 } 89 90 func entryInput(r io.Reader, name string) (*pb.Entry, error) { 91 reader := bufio.NewReader(r) 92 93 username := terminal.Scanln(reader, "Username") 94 url := terminal.Scanln(reader, "URL") 95 expires := terminal.Scanln(reader, "Expires [dd/mm/yy]") 96 notes := terminal.Scanlns(reader, "Notes") 97 98 exp, err := cmdutil.FmtExpires(expires) 99 if err != nil { 100 return nil, err 101 } 102 103 entry := &pb.Entry{ 104 Name: name, 105 Username: username, 106 URL: url, 107 Expires: exp, 108 Notes: notes, 109 } 110 return entry, nil 111 } 112 113 // genPassphrase returns a customized random passphrase. 114 func genPassphrase(opts *phraseOptions) (string, error) { 115 l := atoll.WordList 116 117 if opts.list != "" { 118 opts.list = strings.ReplaceAll(opts.list, " ", "") 119 120 switch strings.ToLower(opts.list) { 121 case "nolist", "no": 122 l = atoll.NoList 123 124 case "wordlist", "word": 125 // Do nothing as it's the default 126 127 case "syllablelist", "syllable": 128 l = atoll.SyllableList 129 130 default: 131 return "", errors.Errorf("invalid list: %q", opts.list) 132 } 133 } 134 135 p := &atoll.Passphrase{ 136 Length: opts.length, 137 Separator: opts.separator, 138 Include: opts.incl, 139 Exclude: opts.excl, 140 List: l, 141 } 142 143 passphrase, err := atoll.NewSecret(p) 144 if err != nil { 145 return "", err 146 } 147 148 return string(passphrase), nil 149 }