github.com/kubeshop/testkube@v1.17.23/pkg/oauth/github.go (about)

     1  package oauth
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"net/url"
    10  	"time"
    11  
    12  	"golang.org/x/oauth2"
    13  	"golang.org/x/oauth2/github"
    14  )
    15  
    16  // NewGithubValidator creates new github validator
    17  func NewGithubValidator(client *http.Client, clientID, clientSecret string, scopes []string) *GithubValidator {
    18  	return &GithubValidator{
    19  		client:       client,
    20  		clientID:     clientID,
    21  		clientSecret: clientSecret,
    22  		scopes:       scopes,
    23  	}
    24  }
    25  
    26  // GithubValidator is github oauth validator
    27  type GithubValidator struct {
    28  	client       *http.Client
    29  	clientID     string
    30  	clientSecret string
    31  	scopes       []string
    32  }
    33  
    34  // Validate validates oauth token
    35  func (v GithubValidator) Validate(accessToken string) error {
    36  	uri := fmt.Sprintf("https://api.github.com/applications/%s/token", v.clientID)
    37  	data := GithubValidatorRequest{
    38  		AccessToken: accessToken,
    39  	}
    40  
    41  	body, err := json.Marshal(data)
    42  	if err != nil {
    43  		return err
    44  	}
    45  
    46  	parsedURI, err := url.Parse(uri)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	parsedURI.User = url.UserPassword(v.clientID, v.clientSecret)
    52  	req, err := http.NewRequest(http.MethodPost, parsedURI.String(), bytes.NewBuffer(body))
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	resp, err := v.client.Do(req)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	defer resp.Body.Close()
    62  
    63  	if resp.StatusCode >= 400 {
    64  		return fmt.Errorf("status: %s", resp.Status)
    65  	}
    66  
    67  	result, err := io.ReadAll(resp.Body)
    68  	if err != nil {
    69  		return err
    70  	}
    71  
    72  	var token GithubValidatorResponse
    73  	if err = json.Unmarshal(result, &token); err != nil {
    74  		return err
    75  	}
    76  
    77  	if token.ExpiresAt != nil && token.ExpiresAt.Before(time.Now()) {
    78  		return fmt.Errorf("token expired at %v", token.ExpiresAt)
    79  	}
    80  
    81  	scopeMap := make(map[string]struct{}, len(token.Scopes))
    82  	for _, scope := range token.Scopes {
    83  		scopeMap[scope] = struct{}{}
    84  	}
    85  
    86  	for _, scope := range v.scopes {
    87  		if _, ok := scopeMap[scope]; !ok {
    88  			return fmt.Errorf("token doesn't contain scope %s", scope)
    89  		}
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // GetEndpoint returns endpoint
    96  func (v GithubValidator) GetEndpoint() oauth2.Endpoint {
    97  	return github.Endpoint
    98  }
    99  
   100  // GithubValidatorRequest contains github validation request
   101  type GithubValidatorRequest struct {
   102  	AccessToken string `json:"access_token"`
   103  }
   104  
   105  // GithubValidatorResponse contains github validation response
   106  type GithubValidatorResponse struct {
   107  	ExpiresAt *time.Time `json:"expires_at"`
   108  	Scopes    []string   `json:"scopes"`
   109  }