PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/go/client/cmd_signup.go

https://gitlab.com/yenny.prathivi/client
Go | 459 lines | 395 code | 60 blank | 4 comment | 70 complexity | e799c59359ee231663d1f4de093dddfc MD5 | raw file
  1. // Copyright 2015 Keybase, Inc. All rights reserved. Use of
  2. // this source code is governed by the included BSD license.
  3. package client
  4. import (
  5. "fmt"
  6. "os"
  7. "golang.org/x/net/context"
  8. "github.com/keybase/cli"
  9. "github.com/keybase/client/go/libcmdline"
  10. "github.com/keybase/client/go/libkb"
  11. keybase1 "github.com/keybase/client/go/protocol"
  12. rpc "github.com/keybase/go-framed-msgpack-rpc"
  13. )
  14. func NewCmdSignup(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
  15. cmd := cli.Command{
  16. Name: "signup",
  17. Usage: "Signup for a new account",
  18. Action: func(c *cli.Context) {
  19. cl.ChooseCommand(NewCmdSignupRunner(g), "signup", c)
  20. },
  21. Flags: []cli.Flag{
  22. cli.StringFlag{
  23. Name: "c, invite-code",
  24. Usage: "Specify an invite code.",
  25. },
  26. cli.StringFlag{
  27. Name: "email",
  28. Usage: "Specify an account email.",
  29. },
  30. cli.StringFlag{
  31. Name: "username",
  32. Usage: "Specify a username.",
  33. },
  34. },
  35. }
  36. cmd.Flags = append(cmd.Flags, restrictedSignupFlags...)
  37. return cmd
  38. }
  39. type PromptFields struct {
  40. email, code, username, passphraseRetry, deviceName *Field
  41. }
  42. func (pf PromptFields) ToList() []*Field {
  43. return []*Field{pf.email, pf.code, pf.username, pf.passphraseRetry, pf.deviceName}
  44. }
  45. type CmdSignup struct {
  46. libkb.Contextified
  47. fields *PromptFields
  48. prompter *Prompter
  49. scli keybase1.SignupClient
  50. ccli keybase1.ConfigClient
  51. code string
  52. requestedInvite bool
  53. fullname string
  54. notes string
  55. passphrase string
  56. storeSecret bool
  57. defaultEmail string
  58. defaultUsername string
  59. defaultPassphrase string
  60. defaultDevice string
  61. doPrompt bool
  62. skipMail bool
  63. genPGP bool
  64. }
  65. func NewCmdSignupRunner(g *libkb.GlobalContext) *CmdSignup {
  66. return &CmdSignup{
  67. Contextified: libkb.NewContextified(g),
  68. doPrompt: true,
  69. }
  70. }
  71. func (s *CmdSignup) SetTest() {
  72. s.skipMail = true
  73. }
  74. func (s *CmdSignup) ParseArgv(ctx *cli.Context) error {
  75. nargs := len(ctx.Args())
  76. var err error
  77. s.code = ctx.String("invite-code")
  78. if s.code == "" {
  79. // For development convenience.
  80. s.code = os.Getenv("KEYBASE_INVITATION_CODE")
  81. }
  82. s.defaultEmail = ctx.String("email")
  83. s.defaultUsername = ctx.String("username")
  84. s.defaultPassphrase = ctx.String("passphrase")
  85. s.defaultDevice = ctx.String("device")
  86. if s.defaultDevice == "" {
  87. s.defaultDevice = "home computer"
  88. }
  89. if ctx.Bool("batch") {
  90. s.fields = &PromptFields{
  91. email: &Field{Value: &s.defaultEmail},
  92. code: &Field{Value: &s.code},
  93. username: &Field{Value: &s.defaultUsername},
  94. deviceName: &Field{Value: &s.defaultDevice},
  95. passphraseRetry: &Field{},
  96. }
  97. s.passphrase = s.defaultPassphrase
  98. s.genPGP = ctx.Bool("pgp")
  99. s.doPrompt = false
  100. } else {
  101. s.doPrompt = true
  102. }
  103. if nargs != 0 {
  104. err = BadArgsError{"Signup doesn't take arguments."}
  105. }
  106. return err
  107. }
  108. func (s *CmdSignup) successMessage() error {
  109. username := s.fields.username.GetValue()
  110. msg := fmt.Sprintf(`
  111. Welcome to keybase.io!
  112. - you are now logged in as %s
  113. - your profile on keybase is https://keybase.io/%s
  114. - type 'keybase help' for more instructions
  115. Keybase is in alpha and we'll be rolling out new features soon. Report bugs
  116. to us at https://github.com/keybase/keybase-issues
  117. Enjoy!
  118. `, username, username)
  119. return s.G().UI.GetTerminalUI().Output(msg)
  120. }
  121. func (s *CmdSignup) Run() (err error) {
  122. s.G().Log.Debug("| Client mode")
  123. if err = s.initClient(); err != nil {
  124. return err
  125. }
  126. if err = s.checkRegistered(); err != nil {
  127. return err
  128. }
  129. if err = s.trySignup(); err != nil {
  130. if _, cce := err.(CleanCancelError); cce {
  131. s.requestedInvite = true
  132. return s.requestInvite()
  133. }
  134. return err
  135. }
  136. s.successMessage()
  137. return nil
  138. }
  139. func (s *CmdSignup) checkRegistered() (err error) {
  140. s.G().Log.Debug("+ clientModeSignupEngine::CheckRegistered")
  141. defer s.G().Log.Debug("- clientModeSignupEngine::CheckRegistered -> %s", libkb.ErrToOk(err))
  142. var rres keybase1.GetCurrentStatusRes
  143. if rres, err = s.ccli.GetCurrentStatus(context.TODO(), 0); err != nil {
  144. return err
  145. }
  146. if !rres.Registered {
  147. return
  148. }
  149. if !s.doPrompt {
  150. return nil
  151. }
  152. prompt := "Already registered; do you want to reregister?"
  153. if rereg, err := s.G().UI.GetTerminalUI().PromptYesNo(PromptDescriptorReregister, prompt, libkb.PromptDefaultNo); err != nil {
  154. return err
  155. } else if !rereg {
  156. return NotConfirmedError{}
  157. }
  158. return nil
  159. }
  160. func (s *CmdSignup) prompt() (err error) {
  161. s.G().Log.Debug("+ prompt")
  162. defer func() {
  163. s.G().Log.Debug("- prompt -> %s", libkb.ErrToOk(err))
  164. }()
  165. if !s.doPrompt {
  166. return nil
  167. }
  168. if s.prompter == nil {
  169. s.MakePrompter()
  170. }
  171. if err = s.prompter.Run(); err != nil {
  172. s.G().Log.Debug("| Prompter failed\n")
  173. return
  174. }
  175. f := s.fields.passphraseRetry
  176. if f.Disabled || libkb.IsYes(f.GetValue()) {
  177. var res keybase1.GetPassphraseRes
  178. res, err = PromptPassphrase(s.G())
  179. if err != nil {
  180. return
  181. }
  182. s.passphrase = res.Passphrase
  183. s.storeSecret = res.StoreSecret
  184. }
  185. return
  186. }
  187. func (s *CmdSignup) trySignup() (err error) {
  188. retry := true
  189. for retry && err == nil {
  190. if err = s.prompt(); err == nil {
  191. retry, err = s.runEngine()
  192. }
  193. }
  194. return err
  195. }
  196. func (s *CmdSignup) runEngine() (retry bool, err error) {
  197. rarg := keybase1.SignupArg{
  198. Username: s.fields.username.GetValue(),
  199. Email: s.fields.email.GetValue(),
  200. InviteCode: s.fields.code.GetValue(),
  201. Passphrase: s.passphrase,
  202. StoreSecret: s.storeSecret,
  203. DeviceName: s.fields.deviceName.GetValue(),
  204. SkipMail: s.skipMail,
  205. GenPGPBatch: s.genPGP,
  206. }
  207. res, err := s.scli.Signup(context.TODO(), rarg)
  208. if err == nil {
  209. return false, nil
  210. }
  211. s.G().Log.Debug("error: %q, type: %T", err, err)
  212. // check to see if the error is a join engine run result:
  213. if res.PassphraseOk {
  214. s.fields.passphraseRetry.Disabled = false
  215. }
  216. if !res.PostOk {
  217. retry, err = s.handlePostError(err)
  218. }
  219. return retry, err
  220. }
  221. func (s *CmdSignup) requestInvitePromptForOk() (err error) {
  222. prompt := "Would you like to be added to the invite request list?"
  223. var invite bool
  224. if invite, err = s.G().UI.GetTerminalUI().PromptYesNo(PromptDescriptorInviteOK, prompt, libkb.PromptDefaultYes); err != nil {
  225. return err
  226. }
  227. if !invite {
  228. return NotConfirmedError{}
  229. }
  230. return nil
  231. }
  232. func (s *CmdSignup) requestInvitePromptForData() error {
  233. fullname := &Field{
  234. Name: "fullname",
  235. Prompt: "Your name",
  236. PromptDescriptor: PromptDescriptorSignupFullName,
  237. }
  238. notes := &Field{
  239. Name: "notes",
  240. Prompt: "Any comments for the team",
  241. PromptDescriptor: PromptDescriptorSignupNotes,
  242. }
  243. fields := []*Field{fullname, notes}
  244. prompter := NewPrompter(fields, s.G().UI.GetTerminalUI())
  245. if err := prompter.Run(); err != nil {
  246. return err
  247. }
  248. s.fullname = fullname.GetValue()
  249. s.notes = notes.GetValue()
  250. return nil
  251. }
  252. func (s *CmdSignup) requestInvite() error {
  253. if err := s.requestInvitePromptForOk(); err != nil {
  254. return err
  255. }
  256. if err := s.requestInvitePromptForData(); err != nil {
  257. return err
  258. }
  259. return s.postInviteRequest()
  260. }
  261. func (s *CmdSignup) MakePrompter() {
  262. code := &Field{
  263. Defval: s.code,
  264. Name: "code",
  265. Prompt: "Your invite code",
  266. Checker: &libkb.CheckInviteCode,
  267. PromptDescriptor: PromptDescriptorSignupCode,
  268. }
  269. if len(s.code) == 0 {
  270. code.Prompt += " (leave blank if you don't have one)"
  271. code.Thrower = func(k, v string) error {
  272. if len(v) == 0 {
  273. return CleanCancelError{}
  274. }
  275. return nil
  276. }
  277. }
  278. passphraseRetry := &Field{
  279. Defval: "n",
  280. Disabled: true,
  281. Name: "passphraseRetry",
  282. Checker: &libkb.CheckYesNo,
  283. Prompt: "Re-enter passphrase",
  284. PromptDescriptor: PromptDescriptorSignupReenterPassphrase,
  285. }
  286. email := &Field{
  287. Defval: s.defaultEmail,
  288. Name: "email",
  289. Prompt: "Your email address",
  290. Checker: &libkb.CheckEmail,
  291. PromptDescriptor: PromptDescriptorSignupEmail,
  292. }
  293. username := &Field{
  294. Defval: s.defaultUsername,
  295. Name: "username",
  296. Prompt: "Your desired username",
  297. Checker: &libkb.CheckUsername,
  298. PromptDescriptor: PromptDescriptorSignupUsername,
  299. }
  300. deviceName := &Field{
  301. Defval: s.defaultDevice,
  302. Name: "devname",
  303. Prompt: "A public name for this device",
  304. Checker: &libkb.CheckDeviceName,
  305. PromptDescriptor: PromptDescriptorSignupDevice,
  306. }
  307. s.fields = &PromptFields{
  308. email: email,
  309. code: code,
  310. username: username,
  311. passphraseRetry: passphraseRetry,
  312. deviceName: deviceName,
  313. }
  314. s.prompter = NewPrompter(s.fields.ToList(), s.G().UI.GetTerminalUI())
  315. }
  316. func (s *CmdSignup) GetUsage() libkb.Usage {
  317. return libkb.Usage{
  318. Config: true,
  319. GpgKeyring: true,
  320. KbKeyring: true,
  321. API: true,
  322. }
  323. }
  324. func (s *CmdSignup) initClient() error {
  325. var err error
  326. if s.scli, err = GetSignupClient(s.G()); err != nil {
  327. return err
  328. }
  329. if s.ccli, err = GetConfigClient(s.G()); err != nil {
  330. return err
  331. }
  332. protocols := []rpc.Protocol{
  333. NewSecretUIProtocol(s.G()),
  334. }
  335. if s.doPrompt {
  336. protocols = append(protocols, NewGPGUIProtocol(s.G()))
  337. protocols = append(protocols, NewLoginUIProtocol(s.G()))
  338. } else {
  339. gpgUI := s.G().UI.GetGPGUI().(GPGUI)
  340. gpgUI.noPrompt = true
  341. protocols = append(protocols, keybase1.GpgUiProtocol(gpgUI))
  342. loginUI := s.G().UI.GetLoginUI().(LoginUI)
  343. loginUI.noPrompt = true
  344. protocols = append(protocols, keybase1.LoginUiProtocol(loginUI))
  345. }
  346. if err = RegisterProtocolsWithContext(protocols, s.G()); err != nil {
  347. return err
  348. }
  349. return nil
  350. }
  351. func (s *CmdSignup) postInviteRequest() (err error) {
  352. rarg := keybase1.InviteRequestArg{
  353. Email: s.fields.email.GetValue(),
  354. Fullname: s.fullname,
  355. Notes: s.notes,
  356. }
  357. err = s.scli.InviteRequest(context.TODO(), rarg)
  358. if err == nil {
  359. s.G().Log.Info("Success! You're on our list, thanks for your interest.")
  360. }
  361. return
  362. }
  363. func (s *CmdSignup) handlePostError(inerr error) (retry bool, err error) {
  364. retry = false
  365. err = inerr
  366. if ase, ok := inerr.(libkb.AppStatusError); ok {
  367. switch ase.Name {
  368. case "BAD_SIGNUP_EMAIL_TAKEN":
  369. v := s.fields.email.Clear()
  370. s.G().Log.Errorf("Email address '%s' already taken", v)
  371. retry = true
  372. err = nil
  373. case "BAD_SIGNUP_USERNAME_TAKEN":
  374. v := s.fields.username.Clear()
  375. s.G().Log.Errorf("Username '%s' already taken", v)
  376. retry = true
  377. err = nil
  378. case "INPUT_ERROR":
  379. if ase.IsBadField("username") {
  380. v := s.fields.username.Clear()
  381. s.G().Log.Errorf("Username '%s' rejected by server", v)
  382. retry = true
  383. err = nil
  384. }
  385. case "BAD_INVITATION_CODE":
  386. v := s.fields.code.Clear()
  387. s.G().Log.Errorf("Bad invitation code '%s' given", v)
  388. retry = true
  389. err = nil
  390. }
  391. }
  392. if !s.doPrompt {
  393. retry = false
  394. }
  395. return
  396. }