github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/cmd/lara/helpers.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "reflect" 7 8 "github.com/hoffie/larasync/api/client" 9 "github.com/hoffie/larasync/helpers/colorhash" 10 "github.com/hoffie/larasync/repository" 11 "golang.org/x/crypto/ssh/terminal" 12 ) 13 14 const ( 15 // PrivateKeySize is the size of the key used for signing. 16 PrivateKeySize = repository.PrivateKeySize 17 // PublicKeySize ist the size of the key used to verify the signature. 18 PublicKeySize = repository.PublicKeySize 19 // EncryptionKeySize is the key size used for encryption purposes. 20 EncryptionKeySize = repository.EncryptionKeySize 21 ) 22 23 // clientFor returns the Client which is configured to communicate 24 // with the given server repository. 25 func (d *Dispatcher) clientFor(r *repository.ClientRepository) (*client.Client, error) { 26 sc, err := r.StateConfig() 27 if err != nil { 28 return nil, fmt.Errorf("unable to load state config (%s)", err) 29 } 30 defaultServer := sc.DefaultServer 31 if defaultServer.URL == "" { 32 return nil, fmt.Errorf("no default server configured (state)") 33 } 34 privKey, err := r.GetSigningPrivateKey() 35 if err != nil { 36 return nil, fmt.Errorf("unable to get signing private key (%s)", err) 37 } 38 client := d.clientForState(sc) 39 client.SetSigningPrivateKey(privKey) 40 41 return client, nil 42 } 43 44 func (d *Dispatcher) clientForState(sc *repository.StateConfig) *client.Client { 45 d.sc = sc 46 defaultServer := sc.DefaultServer 47 return client.New(defaultServer.URL, defaultServer.Fingerprint, 48 d.confirmFingerprint) 49 } 50 51 // promptPassword outputs the given prompt text and waits for a value to be entered 52 // on the input stream. It attempts to do so securely. 53 func (d *Dispatcher) promptPassword(prompt string) ([]byte, error) { 54 switch d.stdin.(type) { 55 case *os.File: 56 return d.promptGetpass(prompt) 57 } 58 return d.promptCleartext(prompt) 59 } 60 61 // promptGetpass reads a password from our input, 62 // attempting to hide the input if possible. 63 func (d *Dispatcher) promptGetpass(prompt string) ([]byte, error) { 64 file := d.stdin.(*os.File) 65 fd := int(file.Fd()) 66 if !terminal.IsTerminal(fd) { 67 return d.promptCleartext(prompt) 68 } 69 d.stdout.Write([]byte(prompt)) 70 defer d.stdout.Write([]byte("\n")) 71 return terminal.ReadPassword(fd) 72 } 73 74 // promptCleartext reads text from our input in the standard way. 75 func (d *Dispatcher) promptCleartext(prompt string) ([]byte, error) { 76 d.stdout.Write([]byte(prompt)) 77 line, err := d.readLine() 78 if err != nil { 79 return nil, err 80 } 81 return line[:len(line)], nil 82 } 83 84 // readLine reads exactly one line; it does not return the delimiter. 85 // The difference to other methods for similar goals (bufio.Scanner or ReadBytes) 86 // is that it only reads as much data as is needed, i.e. it keeps any left-over 87 // data in the original reader for later access. 88 func (d *Dispatcher) readLine() ([]byte, error) { 89 buf := []byte{} 90 oneChar := make([]byte, 1) 91 for { 92 _, err := d.stdin.Read(oneChar) 93 if err != nil { 94 return nil, err 95 } 96 if oneChar[0] == '\n' { 97 return buf, nil 98 } 99 buf = append(buf, oneChar[0]) 100 } 101 } 102 103 // confirmFingerprint is called to ask for permission to connect to a new server. 104 func (d *Dispatcher) confirmFingerprint(fp string) bool { 105 fmt.Fprintf(d.stdout, "You are connecting to this server for the first time.\n") 106 fmt.Fprintf(d.stdout, "To avoid any attacks on the connection between you and the server,\n") 107 fmt.Fprint(d.stdout, "please verify the fingerprint.\n") 108 fmt.Fprint(d.stdout, "Do this by getting the fingerprint on the server using\n") 109 fmt.Fprint(d.stdout, " lara server-fingerprint\n") 110 fmt.Fprint(d.stdout, "or ask the server administrator to provide you with the fingerprint.\n\n") 111 fmt.Fprint(d.stdout, "Only continue connecting when the above mentioned fingerprint matches this value:\n") 112 fmt.Fprintf(d.stdout, "\n%s\n%s\n\n", colorhash.Format(fp), fp) 113 res, err := d.promptCleartext("Do the fingerprints match? (y/N) ") 114 if err != nil { 115 return false 116 } 117 accept := reflect.DeepEqual(res, []byte("y")) 118 if !accept { 119 return false 120 } 121 if d.sc == nil { 122 fmt.Fprintf(d.stderr, 123 "Warning: cannot save fingerprint (no state config)\n") 124 // yes, we return true here nevertheless 125 return true 126 } 127 d.sc.DefaultServer.Fingerprint = fp 128 err = d.sc.Save() 129 if err != nil { 130 fmt.Fprintf(d.stderr, "Warning: failed to save fingerprint (%s)\n", err) 131 } 132 return true 133 }