@@ -16,30 +16,32 @@ import (
1616
1717// InitCommand configures the user's SecretHub account for use on this machine.
1818type InitCommand struct {
19- backupCode string
20- force bool
21- io ui.IO
22- newClient newClientFunc
23- newClientWithoutCredentials func (credentials.Provider ) (secrethub.ClientInterface , error )
24- credentialStore CredentialConfig
25- progressPrinter progress.Printer
19+ backupCode string
20+ setupCode string
21+ force bool
22+ io ui.IO
23+ newUnauthenticatedClient newClientFunc
24+ newClientWithCredentials func (credentials.Provider ) (secrethub.ClientInterface , error )
25+ credentialStore CredentialConfig
26+ progressPrinter progress.Printer
2627}
2728
2829// NewInitCommand creates a new InitCommand.
29- func NewInitCommand (io ui.IO , newClient newClientFunc , newClientWithoutCredentials func (credentials.Provider ) (secrethub.ClientInterface , error ), credentialStore CredentialConfig ) * InitCommand {
30+ func NewInitCommand (io ui.IO , newUnauthenticatedClient newClientFunc , newClientWithCredentials func (credentials.Provider ) (secrethub.ClientInterface , error ), credentialStore CredentialConfig ) * InitCommand {
3031 return & InitCommand {
31- io : io ,
32- newClient : newClient ,
33- newClientWithoutCredentials : newClientWithoutCredentials ,
34- credentialStore : credentialStore ,
35- progressPrinter : progress .NewPrinter (io .Output (), 500 * time .Millisecond ),
32+ io : io ,
33+ newUnauthenticatedClient : newUnauthenticatedClient ,
34+ newClientWithCredentials : newClientWithCredentials ,
35+ credentialStore : credentialStore ,
36+ progressPrinter : progress .NewPrinter (io .Output (), 500 * time .Millisecond ),
3637 }
3738}
3839
3940// Register registers the command, arguments and flags on the provided Registerer.
4041func (cmd * InitCommand ) Register (r command.Registerer ) {
4142 clause := r .Command ("init" , "Initialize the SecretHub client for first use on this device." )
4243 clause .Flag ("backup-code" , "The backup code used to restore an existing account to this device." ).StringVar (& cmd .backupCode )
44+ clause .Flag ("setup-code" , "The setup code used to configure the CLI to use an account created on the website." ).StringVar (& cmd .setupCode )
4345 registerForceFlag (clause ).BoolVar (& cmd .force )
4446
4547 command .BindAction (clause , cmd .Run )
@@ -50,11 +52,16 @@ type InitMode int
5052const (
5153 InitModeSignup InitMode = iota + 1
5254 InitModeBackupCode
55+ InitModeSetupCode
5356)
5457
5558// Run configures the user's SecretHub account for use on this machine.
5659// If an account was already configured, the user is prompted for confirmation to overwrite it.
5760func (cmd * InitCommand ) Run () error {
61+ if cmd .setupCode != "" && cmd .backupCode != "" {
62+ return ErrFlagsConflict ("--backup-code and --setup-code" )
63+ }
64+
5865 credentialPath := cmd .credentialStore .ConfigDir ().Credential ().Path ()
5966
6067 if cmd .credentialStore .ConfigDir ().Credential ().Exists () && ! cmd .force {
@@ -76,7 +83,9 @@ func (cmd *InitCommand) Run() error {
7683 }
7784
7885 var mode InitMode
79- if cmd .backupCode != "" {
86+ if cmd .setupCode != "" {
87+ mode = InitModeSetupCode
88+ } else if cmd .backupCode != "" {
8089 mode = InitModeBackupCode
8190 }
8291
@@ -86,16 +95,18 @@ func (cmd *InitCommand) Run() error {
8695 }
8796 option , err := ui .Choose (cmd .io , "How do you want to initialize your SecretHub account on this device?" ,
8897 []string {
89- "Signup for a new account" ,
98+ "Sign up for a new account" ,
9099 "Use a backup code to recover an existing account" ,
91100 }, 3 )
92101 if err != nil {
93102 return err
94103 }
104+ fmt .Fprintln (cmd .io .Output ())
95105
96106 switch option {
97107 case 0 :
98- mode = InitModeSignup
108+ fmt .Fprintln (cmd .io .Output (), "Go to https://signup.secrethub.io/ and follow the steps to create an account and get it set up on this machine." )
109+ return nil
99110 case 1 :
100111 mode = InitModeBackupCode
101112 }
@@ -105,12 +116,83 @@ func (cmd *InitCommand) Run() error {
105116 case InitModeSignup :
106117 signupCommand := SignUpCommand {
107118 io : cmd .io ,
108- newClient : cmd .newClient ,
119+ newClient : cmd .newUnauthenticatedClient ,
109120 credentialStore : cmd .credentialStore ,
110121 progressPrinter : cmd .progressPrinter ,
111122 force : cmd .force ,
112123 }
113124 return signupCommand .Run ()
125+ case InitModeSetupCode :
126+ setupCode := cmd .setupCode
127+
128+ fmt .Fprintf (cmd .io .Output (), credentialCreationMessage , credentialPath )
129+
130+ // Only prompt for a passphrase when the user hasn't used --force.
131+ // Otherwise, we assume the passphrase was intentionally not
132+ // configured to output a plaintext credential.
133+ var passphrase string
134+ if ! cmd .credentialStore .IsPassphraseSet () && ! cmd .force {
135+ var err error
136+ passphrase , err = askCredentialPassphrase (cmd .io )
137+ if err != nil {
138+ return err
139+ }
140+ }
141+
142+ deviceName , err := promptForDeviceName (cmd .io )
143+ if err != nil {
144+ return err
145+ }
146+
147+ fmt .Fprint (cmd .io .Output (), "Setting up your account..." )
148+ cmd .progressPrinter .Start ()
149+
150+ client , err := cmd .newClientWithCredentials (credentials .NewSetupCode (setupCode ))
151+ if err != nil {
152+ cmd .progressPrinter .Stop ()
153+ return err
154+ }
155+
156+ credential := credentials .CreateKey ()
157+ _ , err = client .Credentials ().Create (credential , deviceName )
158+ if err != nil {
159+ cmd .progressPrinter .Stop ()
160+ return err
161+ }
162+
163+ err = writeNewCredential (credential , passphrase , cmd .credentialStore .ConfigDir ().Credential ())
164+ if err != nil {
165+ cmd .progressPrinter .Stop ()
166+ return err
167+ }
168+
169+ client , err = cmd .newClientWithCredentials (credential )
170+ if err != nil {
171+ cmd .progressPrinter .Stop ()
172+ return err
173+ }
174+
175+ me , err := client .Me ().GetUser ()
176+ if err != nil {
177+ cmd .progressPrinter .Stop ()
178+ return err
179+ }
180+
181+ secretPath , err := createStartRepo (client , me .Username , me .FullName )
182+ if err != nil {
183+ cmd .progressPrinter .Stop ()
184+ return err
185+ }
186+ cmd .progressPrinter .Stop ()
187+ fmt .Fprint (cmd .io .Output (), "Created your account.\n \n " )
188+
189+ err = createWorkspace (client , cmd .io , "" , "" , cmd .progressPrinter )
190+ if err != nil {
191+ return err
192+ }
193+
194+ fmt .Fprintf (cmd .io .Output (), "Setup complete. To read your first secret, run:\n \n secrethub read %s\n \n " , secretPath )
195+ return nil
114196 case InitModeBackupCode :
115197 backupCode := cmd .backupCode
116198
@@ -122,7 +204,7 @@ func (cmd *InitCommand) Run() error {
122204 }
123205 }
124206
125- client , err := cmd .newClientWithoutCredentials (credentials .UseBackupCode (backupCode ))
207+ client , err := cmd .newClientWithCredentials (credentials .UseBackupCode (backupCode ))
126208 if err != nil {
127209 return err
128210 }
@@ -146,19 +228,9 @@ func (cmd *InitCommand) Run() error {
146228 return nil
147229 }
148230
149- deviceName := ""
150- question := "What is the name of this device?"
151- hostName , err := os .Hostname ()
152- if err == nil {
153- deviceName , err = ui .AskWithDefault (cmd .io , question , hostName )
154- if err != nil {
155- return err
156- }
157- } else {
158- deviceName , err = ui .Ask (cmd .io , question )
159- if err != nil {
160- return err
161- }
231+ deviceName , err := promptForDeviceName (cmd .io )
232+ if err != nil {
233+ return err
162234 }
163235
164236 // Only prompt for a passphrase when the user hasn't used --force.
@@ -167,7 +239,7 @@ func (cmd *InitCommand) Run() error {
167239 var passphrase string
168240 if ! cmd .credentialStore .IsPassphraseSet () && ! cmd .force {
169241 var err error
170- passphrase , err = ui . AskPassphrase (cmd .io , "Please enter a passphrase to protect your local credential (leave empty for no passphrase): " , "Enter the same passphrase again: " , 3 )
242+ passphrase , err = askCredentialPassphrase (cmd .io )
171243 if err != nil {
172244 return err
173245 }
@@ -197,3 +269,21 @@ func (cmd *InitCommand) Run() error {
197269 return errors .New ("invalid option" )
198270 }
199271}
272+
273+ func promptForDeviceName (io ui.IO ) (string , error ) {
274+ deviceName := ""
275+ question := "What is the name of this device?"
276+ hostName , err := os .Hostname ()
277+ if err == nil {
278+ deviceName , err = ui .AskWithDefault (io , question , hostName )
279+ if err != nil {
280+ return "" , err
281+ }
282+ } else {
283+ deviceName , err = ui .Ask (io , question )
284+ if err != nil {
285+ return "" , err
286+ }
287+ }
288+ return deviceName , nil
289+ }
0 commit comments