github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_login.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package main 21 22 import ( 23 "bufio" 24 "fmt" 25 "strings" 26 27 "github.com/jessevdk/go-flags" 28 29 "github.com/snapcore/snapd/client" 30 "github.com/snapcore/snapd/i18n" 31 ) 32 33 type cmdLogin struct { 34 clientMixin 35 Positional struct { 36 Email string 37 } `positional-args:"yes"` 38 } 39 40 var shortLoginHelp = i18n.G("Authenticate to snapd and the store") 41 42 var longLoginHelp = i18n.G(` 43 The login command authenticates the user to snapd and the snap store, and saves 44 credentials into the ~/.snap/auth.json file. Further communication with snapd 45 will then be made using those credentials. 46 47 It's not necessary to log in to interact with snapd. Doing so, however, enables 48 purchasing of snaps using 'snap buy', as well as some some developer-oriented 49 features as detailed in the help for the find, install and refresh commands. 50 51 An account can be set up at https://login.ubuntu.com 52 `) 53 54 func init() { 55 addCommand("login", 56 shortLoginHelp, 57 longLoginHelp, 58 func() flags.Commander { 59 return &cmdLogin{} 60 }, nil, []argDesc{{ 61 // TRANSLATORS: This is a noun, and it needs to begin with < and end with > 62 name: i18n.G("<email>"), 63 // TRANSLATORS: This should not start with a lowercase letter (unless it's "login.ubuntu.com") 64 desc: i18n.G("The login.ubuntu.com email to login as"), 65 }}) 66 } 67 68 func requestLoginWith2faRetry(cli *client.Client, email, password string) error { 69 var otp []byte 70 var err error 71 72 var msgs = [3]string{ 73 i18n.G("Two-factor code: "), 74 i18n.G("Bad code. Try again: "), 75 i18n.G("Wrong again. Once more: "), 76 } 77 78 reader := bufio.NewReader(nil) 79 80 for i := 0; ; i++ { 81 // first try is without otp 82 _, err = cli.Login(email, password, string(otp)) 83 if i >= len(msgs) || !client.IsTwoFactorError(err) { 84 return err 85 } 86 87 reader.Reset(Stdin) 88 fmt.Fprint(Stdout, msgs[i]) 89 // the browser shows it as well (and Sergio wants to see it ;) 90 otp, _, err = reader.ReadLine() 91 if err != nil { 92 return err 93 } 94 } 95 } 96 97 func requestLogin(cli *client.Client, email string) error { 98 fmt.Fprintf(Stdout, i18n.G("Password of %q: "), email) 99 password, err := ReadPassword(0) 100 fmt.Fprint(Stdout, "\n") 101 if err != nil { 102 return err 103 } 104 105 // strings.TrimSpace needed because we get \r from the pty in the tests 106 return requestLoginWith2faRetry(cli, email, strings.TrimSpace(string(password))) 107 } 108 109 func (x *cmdLogin) Execute(args []string) error { 110 if len(args) > 0 { 111 return ErrExtraArgs 112 } 113 114 //TRANSLATORS: after the "... at" follows a URL in the next line 115 fmt.Fprint(Stdout, i18n.G("Personal information is handled as per our privacy notice at\n")) 116 fmt.Fprint(Stdout, "https://www.ubuntu.com/legal/dataprivacy/snap-store\n\n") 117 118 email := x.Positional.Email 119 if email == "" { 120 fmt.Fprint(Stdout, i18n.G("Email address: ")) 121 in, _, err := bufio.NewReader(Stdin).ReadLine() 122 if err != nil { 123 return err 124 } 125 email = string(in) 126 } 127 128 err := requestLogin(x.client, email) 129 if err != nil { 130 return err 131 } 132 fmt.Fprintln(Stdout, i18n.G("Login successful")) 133 134 return nil 135 }