github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/server/oauth_github.go (about) 1 package server 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 9 "github.com/sirupsen/logrus" 10 "golang.org/x/oauth2" 11 12 "github.com/pyroscope-io/pyroscope/pkg/config" 13 ) 14 15 type oauthHandlerGithub struct { 16 oauthBase 17 allowedOrganizations []string 18 } 19 20 func newGithubHandler(cfg config.GithubOauth, baseURL string, log *logrus.Logger) (*oauthHandlerGithub, error) { 21 authURL, err := url.Parse(cfg.AuthURL) 22 if err != nil { 23 return nil, err 24 } 25 26 h := &oauthHandlerGithub{ 27 oauthBase: oauthBase{ 28 config: &oauth2.Config{ 29 ClientID: cfg.ClientID, 30 ClientSecret: cfg.ClientSecret, 31 Scopes: []string{"read:user", "user:email", "read:org"}, 32 Endpoint: oauth2.Endpoint{AuthURL: cfg.AuthURL, TokenURL: cfg.TokenURL}, 33 }, 34 authURL: authURL, 35 log: log, 36 callbackRoute: "/auth/github/callback", 37 redirectRoute: "/auth/github/redirect", 38 apiURL: "https://api.github.com", 39 baseURL: baseURL, 40 }, 41 allowedOrganizations: cfg.AllowedOrganizations, 42 } 43 44 if cfg.RedirectURL != "" { 45 h.config.RedirectURL = cfg.RedirectURL 46 } 47 48 return h, nil 49 } 50 51 type githubOrganizations struct { 52 Login string 53 } 54 55 func (o oauthHandlerGithub) userAuth(client *http.Client) (extUserInfo, error) { 56 type userProfileResponse struct { 57 ID int64 58 Email string 59 Login string 60 AvatarURL string 61 } 62 63 resp, err := client.Get(o.apiURL + "/user") 64 if err != nil { 65 return extUserInfo{}, fmt.Errorf("failed to get oauth user info: %w", err) 66 } 67 defer resp.Body.Close() 68 69 var userProfile userProfileResponse 70 err = json.NewDecoder(resp.Body).Decode(&userProfile) 71 if err != nil { 72 return extUserInfo{}, fmt.Errorf("failed to decode user profile response: %w", err) 73 } 74 u := extUserInfo{ 75 Name: userProfile.Login, 76 Email: userProfile.Email, 77 } 78 79 if len(o.allowedOrganizations) == 0 { 80 return u, nil 81 } 82 83 organizations, err := o.fetchOrganizations(client) 84 if err != nil { 85 return extUserInfo{}, fmt.Errorf("failed to get organizations: %w", err) 86 } 87 88 for _, allowed := range o.allowedOrganizations { 89 for _, member := range organizations { 90 if member.Login == allowed { 91 return u, nil 92 } 93 } 94 } 95 96 return extUserInfo{}, errForbidden 97 } 98 99 func (o oauthHandlerGithub) fetchOrganizations(client *http.Client) ([]githubOrganizations, error) { 100 orgsURL := o.apiURL + "/user/orgs" 101 more := true 102 organizations := make([]githubOrganizations, 0) 103 104 for more { 105 resp, err := client.Get(orgsURL) 106 if err != nil { 107 return nil, err 108 } 109 defer resp.Body.Close() 110 111 var orgs []githubOrganizations 112 err = json.NewDecoder(resp.Body).Decode(&orgs) 113 if err != nil { 114 return nil, err 115 } 116 117 organizations = append(organizations, orgs...) 118 119 orgsURL, more = hasMoreLinkResults(resp.Header) 120 if err != nil { 121 return nil, err 122 } 123 } 124 125 return organizations, nil 126 } 127 128 func (o oauthHandlerGithub) getOauthBase() oauthBase { 129 return o.oauthBase 130 }