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 }