github.com/trevoraustin/hub@v2.2.0-preview1.0.20141105230840-96d8bfc654cc+incompatible/github/config.go (about) 1 package github 2 3 import ( 4 "bufio" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "strconv" 10 11 "github.com/github/hub/utils" 12 "github.com/howeyc/gopass" 13 ) 14 15 var ( 16 defaultConfigsFile = filepath.Join(os.Getenv("HOME"), ".config", "hub") 17 ) 18 19 type yamlHost struct { 20 User string `yaml:"user"` 21 OAuthToken string `yaml:"oauth_token"` 22 Protocol string `yaml:"protocol"` 23 } 24 25 type yamlConfig map[string][]yamlHost 26 27 type Host struct { 28 Host string `toml:"host"` 29 User string `toml:"user"` 30 AccessToken string `toml:"access_token"` 31 Protocol string `toml:"protocol"` 32 } 33 34 type Config struct { 35 Hosts []Host `toml:"hosts"` 36 } 37 38 func (c *Config) PromptForHost(host string) (h *Host, err error) { 39 h = c.Find(host) 40 if h != nil { 41 return 42 } 43 44 user := c.PromptForUser() 45 pass := c.PromptForPassword(host, user) 46 47 client := NewClient(host) 48 token, e := client.FindOrCreateToken(user, pass, "") 49 if e != nil { 50 if ae, ok := e.(*AuthError); ok && ae.Is2FAError() { 51 code := c.PromptForOTP() 52 token, err = client.FindOrCreateToken(user, pass, code) 53 } else { 54 err = e 55 } 56 } 57 58 if err != nil { 59 return 60 } 61 62 client.Host.AccessToken = token 63 currentUser, err := client.CurrentUser() 64 if err != nil { 65 return 66 } 67 68 h = &Host{ 69 Host: host, 70 User: currentUser.Login, 71 AccessToken: token, 72 Protocol: "https", 73 } 74 c.Hosts = append(c.Hosts, *h) 75 err = newConfigService().Save(configsFile(), c) 76 77 return 78 } 79 80 func (c *Config) PromptForUser() (user string) { 81 user = os.Getenv("GITHUB_USER") 82 if user != "" { 83 return 84 } 85 86 fmt.Printf("%s username: ", GitHubHost) 87 user = c.scanLine() 88 89 return 90 } 91 92 func (c *Config) PromptForPassword(host, user string) (pass string) { 93 pass = os.Getenv("GITHUB_PASSWORD") 94 if pass != "" { 95 return 96 } 97 98 fmt.Printf("%s password for %s (never stored): ", host, user) 99 if isTerminal(os.Stdout.Fd()) { 100 pass = string(gopass.GetPasswd()) 101 } else { 102 pass = c.scanLine() 103 } 104 105 return 106 } 107 108 func (c *Config) PromptForOTP() string { 109 fmt.Print("two-factor authentication code: ") 110 return c.scanLine() 111 } 112 113 func (c *Config) scanLine() string { 114 var line string 115 scanner := bufio.NewScanner(os.Stdin) 116 if scanner.Scan() { 117 line = scanner.Text() 118 } 119 utils.Check(scanner.Err()) 120 121 return line 122 } 123 124 func (c *Config) Find(host string) *Host { 125 for _, h := range c.Hosts { 126 if h.Host == host { 127 return &h 128 } 129 } 130 131 return nil 132 } 133 134 func (c *Config) selectHost() *Host { 135 options := len(c.Hosts) 136 137 if options == 1 { 138 return &c.Hosts[0] 139 } 140 141 prompt := "Select host:\n" 142 for idx, host := range c.Hosts { 143 prompt += fmt.Sprintf(" %d. %s\n", idx+1, host.Host) 144 } 145 prompt += fmt.Sprint("> ") 146 147 fmt.Printf(prompt) 148 index := c.scanLine() 149 i, err := strconv.Atoi(index) 150 if err != nil || i < 1 || i > options { 151 utils.Check(fmt.Errorf("Error: must enter a number [1-%d]", options)) 152 } 153 154 return &c.Hosts[i-1] 155 } 156 157 func configsFile() string { 158 configsFile := os.Getenv("GH_CONFIG") 159 if configsFile == "" { 160 configsFile = defaultConfigsFile 161 } 162 163 return configsFile 164 } 165 166 func CurrentConfig() *Config { 167 c := &Config{} 168 newConfigService().Load(configsFile(), c) 169 170 return c 171 } 172 173 func (c *Config) DefaultHost() (host *Host, err error) { 174 if GitHubHostEnv != "" { 175 host, err = c.PromptForHost(GitHubHostEnv) 176 } else if len(c.Hosts) > 0 { 177 host = c.selectHost() 178 } else { 179 host, err = c.PromptForHost(DefaultGitHubHost()) 180 } 181 182 return 183 } 184 185 // Public for testing purpose 186 func CreateTestConfigs(user, token string) *Config { 187 f, _ := ioutil.TempFile("", "test-config") 188 defaultConfigsFile = f.Name() 189 190 host := Host{ 191 User: "jingweno", 192 AccessToken: "123", 193 Host: GitHubHost, 194 } 195 196 c := &Config{Hosts: []Host{host}} 197 err := newConfigService().Save(f.Name(), c) 198 if err != nil { 199 panic(err) 200 } 201 202 return c 203 }