diff options
author | Michael Muré <batolettre@gmail.com> | 2018-09-21 18:23:46 +0200 |
---|---|---|
committer | Michael Muré <batolettre@gmail.com> | 2018-09-21 18:53:44 +0200 |
commit | 921cd18cf98ecfc1f7fa82f57d64f1b1f9077e64 (patch) | |
tree | dfd7fea5be54c6020ed6773fbdefbaeb5cd2a78e /bridge/github/auth.go | |
parent | 82eaceffc1d750832a2a66f206749d2dca968cce (diff) | |
download | git-bug-921cd18cf98ecfc1f7fa82f57d64f1b1f9077e64.tar.gz |
bridge: better interfaces, working github configurator
Diffstat (limited to 'bridge/github/auth.go')
-rw-r--r-- | bridge/github/auth.go | 244 |
1 files changed, 0 insertions, 244 deletions
diff --git a/bridge/github/auth.go b/bridge/github/auth.go deleted file mode 100644 index b721df7f..00000000 --- a/bridge/github/auth.go +++ /dev/null @@ -1,244 +0,0 @@ -package github - -import ( - "bufio" - "bytes" - "encoding/json" - "fmt" - "io" - "io/ioutil" - "math/rand" - "net/http" - "os" - "strings" - "syscall" - "time" - - "golang.org/x/crypto/ssh/terminal" -) - -const githubV3Url = "https://api.github.com" - -func Configure() (map[string]string, error) { - fmt.Println("git-bug will generate an access token in your Github profile.") - // fmt.Println("The token will have the \"repo\" permission, giving it read/write access to your repositories and issues. There is no narrower scope available, sorry :-|") - fmt.Println() - - tokenName, err := promptTokenName() - if err != nil { - return nil, err - } - - fmt.Println() - - username, err := promptUsername() - if err != nil { - return nil, err - } - - fmt.Println() - - password, err := promptPassword() - if err != nil { - return nil, err - } - - fmt.Println() - - // Attempt to authenticate and create a token - - var note string - if tokenName == "" { - note = "git-bug" - } else { - note = fmt.Sprintf("git-bug - %s", tokenName) - } - - resp, err := requestToken(note, username, password) - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - if resp.StatusCode == http.StatusCreated { - return decodeBody(resp.Body) - } - - // Handle 2FA is needed - OTPHeader := resp.Header.Get("X-GitHub-OTP") - if resp.StatusCode == http.StatusUnauthorized && OTPHeader != "" { - otpCode, err := prompt2FA() - if err != nil { - return nil, err - } - - resp, err = requestTokenWith2FA(note, username, password, otpCode) - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - if resp.StatusCode == http.StatusCreated { - return decodeBody(resp.Body) - } - } - - b, _ := ioutil.ReadAll(resp.Body) - fmt.Printf("Error %v: %v\n", resp.StatusCode, string(b)) - - return nil, nil -} - -func requestToken(note, username, password string) (*http.Response, error) { - return requestTokenWith2FA(note, username, password, "") -} - -func requestTokenWith2FA(note, username, password, otpCode string) (*http.Response, error) { - url := fmt.Sprintf("%s/authorizations", githubV3Url) - params := struct { - Scopes []string `json:"scopes"` - Note string `json:"note"` - Fingerprint string `json:"fingerprint"` - }{ - // Scopes: []string{"repo"}, - Note: note, - Fingerprint: randomFingerprint(), - } - - data, err := json.Marshal(params) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) - if err != nil { - return nil, err - } - - req.SetBasicAuth(username, password) - req.Header.Set("Content-Type", "application/json") - - if otpCode != "" { - req.Header.Set("X-GitHub-OTP", otpCode) - } - - client := http.Client{} - - return client.Do(req) -} - -func decodeBody(body io.ReadCloser) (map[string]string, error) { - data, _ := ioutil.ReadAll(body) - - aux := struct { - Token string `json:"token"` - }{} - - err := json.Unmarshal(data, &aux) - if err != nil { - return nil, err - } - - return map[string]string{ - "token": aux.Token, - }, nil -} - -func randomFingerprint() string { - // Doesn't have to be crypto secure, it's just to avoid token collision - rand.Seed(time.Now().UnixNano()) - var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - b := make([]rune, 32) - for i := range b { - b[i] = letterRunes[rand.Intn(len(letterRunes))] - } - return string(b) -} - -func promptUsername() (string, error) { - for { - fmt.Println("username:") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return "", err - } - - line = strings.TrimRight(line, "\n") - - ok, err := validateUsername(line) - if err != nil { - return "", err - } - if ok { - return line, nil - } - - fmt.Println("invalid username") - } -} - -func promptTokenName() (string, error) { - fmt.Println("To help distinguish the token, you can optionally provide a description") - fmt.Println("The token will be named \"git-bug - <description>\"") - fmt.Println("description:") - - line, err := bufio.NewReader(os.Stdin).ReadString('\n') - if err != nil { - return "", err - } - - return strings.TrimRight(line, "\n"), nil -} - -func validateUsername(username string) (bool, error) { - url := fmt.Sprintf("%s/users/%s", githubV3Url, username) - - resp, err := http.Get(url) - if err != nil { - return false, err - } - - err = resp.Body.Close() - if err != nil { - return false, err - } - - return resp.StatusCode == http.StatusOK, nil -} - -func promptPassword() (string, error) { - for { - fmt.Println("password:") - - bytePassword, err := terminal.ReadPassword(int(syscall.Stdin)) - if err != nil { - return "", err - } - - if len(bytePassword) > 0 { - return string(bytePassword), nil - } - - fmt.Println("password is empty") - } -} - -func prompt2FA() (string, error) { - for { - fmt.Println("two-factor authentication code:") - - byte2fa, err := terminal.ReadPassword(int(syscall.Stdin)) - if err != nil { - return "", err - } - - if len(byte2fa) > 0 { - return string(byte2fa), nil - } - - fmt.Println("code is empty") - } -} |