github.com/secman-team/gh-api@v1.8.2/pkg/cmd/auth/shared/oauth_scopes.go (about) 1 package shared 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "net/http" 8 "strings" 9 10 "github.com/secman-team/gh-api/api" 11 "github.com/secman-team/gh-api/core/ghinstance" 12 ) 13 14 type MissingScopesError struct { 15 MissingScopes []string 16 } 17 18 func (e MissingScopesError) Error() string { 19 var missing []string 20 for _, s := range e.MissingScopes { 21 missing = append(missing, fmt.Sprintf("'%s'", s)) 22 } 23 scopes := strings.Join(missing, ", ") 24 25 if len(e.MissingScopes) == 1 { 26 return "missing required scope " + scopes 27 } 28 return "missing required scopes " + scopes 29 } 30 31 type httpClient interface { 32 Do(*http.Request) (*http.Response, error) 33 } 34 35 func HasMinimumScopes(httpClient httpClient, hostname, authToken string) error { 36 apiEndpoint := ghinstance.RESTPrefix(hostname) 37 38 req, err := http.NewRequest("GET", apiEndpoint, nil) 39 if err != nil { 40 return err 41 } 42 43 req.Header.Set("Authorization", "token "+authToken) 44 45 res, err := httpClient.Do(req) 46 if err != nil { 47 return err 48 } 49 50 defer func() { 51 // Ensure the response body is fully read and closed 52 // before we reconnect, so that we reuse the same TCPconnection. 53 _, _ = io.Copy(ioutil.Discard, res.Body) 54 res.Body.Close() 55 }() 56 57 if res.StatusCode != 200 { 58 return api.HandleHTTPError(res) 59 } 60 61 scopesHeader := res.Header.Get("X-Oauth-Scopes") 62 if scopesHeader == "" { 63 // if the token reports no scopes, assume that it's an integration token and give up on 64 // detecting its capabilities 65 return nil 66 } 67 68 search := map[string]bool{ 69 "repo": false, 70 "read:org": false, 71 "admin:org": false, 72 } 73 for _, s := range strings.Split(scopesHeader, ",") { 74 search[strings.TrimSpace(s)] = true 75 } 76 77 var missingScopes []string 78 if !search["repo"] { 79 missingScopes = append(missingScopes, "repo") 80 } 81 82 if !search["read:org"] && !search["write:org"] && !search["admin:org"] { 83 missingScopes = append(missingScopes, "read:org") 84 } 85 86 if len(missingScopes) > 0 { 87 return &MissingScopesError{MissingScopes: missingScopes} 88 } 89 return nil 90 }