go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/cli/components/password.go (about) 1 // Copyright (c) Mondoo, Inc. 2 // SPDX-License-Identifier: BUSL-1.1 3 4 package components 5 6 import ( 7 "errors" 8 "os" 9 10 input "github.com/charmbracelet/bubbles/textinput" 11 tea "github.com/charmbracelet/bubbletea" 12 "github.com/mattn/go-isatty" 13 ) 14 15 type passwordErrMsg error 16 17 type password struct { 18 textInput input.Model 19 err error 20 onComplete func(res string, aborted bool) 21 } 22 23 // A bubbletea password input component 24 // 25 // passwordModel := components.NewPasswordModel("root@192.168.178.141's password: ", func(res string, aborted bool) { 26 // // output the pwd 27 // fmt.Println(password) 28 // }) 29 // 30 // p := tea.NewProgram(passwordModel) 31 // 32 // if err := p.Start(); err != nil { 33 // panic(err) 34 // } 35 func NewPasswordModel(prompt string, onComplete func(res string, aborted bool)) password { 36 inputModel := input.NewModel() 37 inputModel.Prompt = prompt 38 inputModel.Focus() 39 inputModel.EchoMode = input.EchoNone 40 inputModel.CharLimit = 156 41 inputModel.Width = 20 42 43 return password{ 44 textInput: inputModel, 45 err: nil, 46 onComplete: onComplete, 47 } 48 } 49 50 func (m password) Init() tea.Cmd { 51 return input.Blink 52 } 53 54 func (m password) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 55 var cmd tea.Cmd 56 57 switch msg := msg.(type) { 58 case tea.KeyMsg: 59 aborted := false 60 switch msg.Type { 61 case tea.KeyCtrlC: 62 aborted = true 63 fallthrough 64 case tea.KeyEsc: 65 aborted = true 66 fallthrough 67 case tea.KeyEnter: 68 m.onComplete(m.textInput.Value(), aborted) 69 return m, tea.Quit 70 } 71 72 // We handle errors just like any other message 73 case passwordErrMsg: 74 m.err = msg 75 return m, nil 76 } 77 78 m.textInput, cmd = m.textInput.Update(msg) 79 return m, cmd 80 } 81 82 func (m password) View() string { 83 return m.textInput.View() + "\n" 84 } 85 86 // AskPassword will only prompt the user for a password if they are on a TTY. 87 func AskPassword(prompt string) (string, error) { 88 // check if password is set 89 if !isatty.IsTerminal(os.Stdout.Fd()) { 90 return "", errors.New("asking passwords is only supported when used with an interactive terminal (TTY)") 91 } 92 93 // ask user for password 94 var res string = "" 95 passwordModel := NewPasswordModel(prompt, func(userPassword string, aborted bool) { 96 res = userPassword 97 if aborted { 98 os.Exit(1) 99 } 100 }) 101 102 p := tea.NewProgram(passwordModel, tea.WithInputTTY()) 103 if _, err := p.Run(); err != nil { 104 return res, err 105 } 106 107 return res, nil 108 }