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  }