github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/server/oauth_gitlab.go (about)

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