github.com/google/go-github/v33@v33.0.0/test/integration/authorizations_test.go (about)

     1  // Copyright 2016 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // +build integration
     7  
     8  package integration
     9  
    10  import (
    11  	"context"
    12  	"math/rand"
    13  	"os"
    14  	"strconv"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/google/go-github/v33/github"
    20  )
    21  
    22  const msgEnvMissing = "Skipping test because the required environment variable (%v) is not present."
    23  const envKeyGitHubUsername = "GITHUB_USERNAME"
    24  const envKeyGitHubPassword = "GITHUB_PASSWORD"
    25  const envKeyClientID = "GITHUB_CLIENT_ID"
    26  const envKeyClientSecret = "GITHUB_CLIENT_SECRET"
    27  const envKeyAccessToken = "GITHUB_ACCESS_TOKEN"
    28  const InvalidTokenValue = "iamnotacroken"
    29  
    30  // TestAuthorizationsAppOperations tests the application/token related operations, such
    31  // as creating, testing, resetting and revoking application OAuth tokens.
    32  func TestAuthorizationsAppOperations(t *testing.T) {
    33  
    34  	appAuthenticatedClient := getOAuthAppClient(t)
    35  
    36  	// We know these vars are set because getOAuthAppClient would have
    37  	// skipped the test by now
    38  	clientID := os.Getenv(envKeyClientID)
    39  	accessToken := os.Getenv(envKeyAccessToken)
    40  
    41  	// Verify the token
    42  	appAuth, resp, err := appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, accessToken)
    43  	failOnError(t, err)
    44  	failIfNotStatusCode(t, resp, 200)
    45  
    46  	// Quick sanity check
    47  	if *appAuth.Token != accessToken {
    48  		t.Fatal("The returned auth/token does not match.")
    49  	}
    50  
    51  	// Let's verify that we get a 404 for a non-existent token
    52  	_, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, InvalidTokenValue)
    53  	if err == nil {
    54  		t.Fatal("An error should have been returned because of the invalid token.")
    55  	}
    56  	failIfNotStatusCode(t, resp, 404)
    57  
    58  	// Let's reset the token
    59  	resetAuth, resp, err := appAuthenticatedClient.Authorizations.Reset(context.Background(), clientID, accessToken)
    60  	failOnError(t, err)
    61  	failIfNotStatusCode(t, resp, 200)
    62  
    63  	// Let's verify that we get a 404 for a non-existent token
    64  	_, resp, err = appAuthenticatedClient.Authorizations.Reset(context.Background(), clientID, InvalidTokenValue)
    65  	if err == nil {
    66  		t.Fatal("An error should have been returned because of the invalid token.")
    67  	}
    68  	failIfNotStatusCode(t, resp, 404)
    69  
    70  	// Verify that the token has changed
    71  	if *resetAuth.Token == accessToken {
    72  		t.Fatal("The reset token should be different from the original.")
    73  	}
    74  
    75  	// Verify that we do have a token value
    76  	if *resetAuth.Token == "" {
    77  		t.Fatal("A token value should have been returned.")
    78  	}
    79  
    80  	// Verify that the original token is now invalid
    81  	_, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, accessToken)
    82  	if err == nil {
    83  		t.Fatal("The original token should be invalid.")
    84  	}
    85  	failIfNotStatusCode(t, resp, 404)
    86  
    87  	// Check that the reset token is valid
    88  	_, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *resetAuth.Token)
    89  	failOnError(t, err)
    90  	failIfNotStatusCode(t, resp, 200)
    91  
    92  	// Let's revoke the token
    93  	resp, err = appAuthenticatedClient.Authorizations.Revoke(context.Background(), clientID, *resetAuth.Token)
    94  	failOnError(t, err)
    95  	failIfNotStatusCode(t, resp, 204)
    96  
    97  	// Sleep for two seconds... I've seen cases where the revocation appears not
    98  	// to have take place immediately.
    99  	time.Sleep(time.Second * 2)
   100  
   101  	// Now, the reset token should also be invalid
   102  	_, resp, err = appAuthenticatedClient.Authorizations.Check(context.Background(), clientID, *resetAuth.Token)
   103  	if err == nil {
   104  		t.Fatal("The reset token should be invalid.")
   105  	}
   106  	failIfNotStatusCode(t, resp, 404)
   107  }
   108  
   109  // generatePersonalAuthTokenRequest is a helper function that generates an
   110  // AuthorizationRequest for a Personal Access Token (no client id).
   111  func generatePersonalAuthTokenRequest() *github.AuthorizationRequest {
   112  
   113  	rand := randString()
   114  	auth := github.AuthorizationRequest{
   115  		Note:        github.String("Personal token: Note generated by test: " + rand),
   116  		Scopes:      []github.Scope{github.ScopePublicRepo},
   117  		Fingerprint: github.String("Personal token: Fingerprint generated by test: " + rand),
   118  	}
   119  
   120  	return &auth
   121  }
   122  
   123  // generatePersonalAuthTokenRequest is a helper function that generates an
   124  // AuthorizationRequest for an OAuth application Token (uses client id).
   125  func generateAppAuthTokenRequest(clientID string, clientSecret string) *github.AuthorizationRequest {
   126  
   127  	rand := randString()
   128  	auth := github.AuthorizationRequest{
   129  		Note:         github.String("App token: Note generated by test: " + rand),
   130  		Scopes:       []github.Scope{github.ScopePublicRepo},
   131  		Fingerprint:  github.String("App token: Fingerprint generated by test: " + rand),
   132  		ClientID:     github.String(clientID),
   133  		ClientSecret: github.String(clientSecret),
   134  	}
   135  
   136  	return &auth
   137  }
   138  
   139  // randString returns a (kinda) random string for uniqueness purposes.
   140  func randString() string {
   141  	return strconv.FormatInt(rand.NewSource(time.Now().UnixNano()).Int63(), 10)
   142  }
   143  
   144  // failOnError invokes t.Fatal() if err is present.
   145  func failOnError(t *testing.T, err error) {
   146  
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  }
   151  
   152  // failIfNotStatusCode invokes t.Fatal() if the response's status code doesn't match the expected code.
   153  func failIfNotStatusCode(t *testing.T, resp *github.Response, expectedCode int) {
   154  
   155  	if resp.StatusCode != expectedCode {
   156  		t.Fatalf("Expected HTTP status code [%v] but received [%v]", expectedCode, resp.StatusCode)
   157  	}
   158  
   159  }
   160  
   161  // getUserPassClient returns a GitHub client for authorization testing. The client
   162  // uses BasicAuth via GH username and password passed in environment variables
   163  // (and will skip the calling test if those vars are not present).
   164  func getUserPassClient(t *testing.T) *github.Client {
   165  	username, ok := os.LookupEnv(envKeyGitHubUsername)
   166  	if !ok {
   167  		t.Skipf(msgEnvMissing, envKeyGitHubUsername)
   168  	}
   169  
   170  	password, ok := os.LookupEnv(envKeyGitHubPassword)
   171  	if !ok {
   172  		t.Skipf(msgEnvMissing, envKeyGitHubPassword)
   173  	}
   174  
   175  	tp := github.BasicAuthTransport{
   176  		Username: strings.TrimSpace(username),
   177  		Password: strings.TrimSpace(password),
   178  	}
   179  
   180  	return github.NewClient(tp.Client())
   181  }
   182  
   183  // getOAuthAppClient returns a GitHub client for authorization testing. The client
   184  // uses BasicAuth, but instead of username and password, it uses the client id
   185  // and client secret passed in via environment variables
   186  // (and will skip the calling test if those vars are not present). Certain API operations (check
   187  // an authorization; reset an authorization; revoke an authorization for an app)
   188  // require this authentication mechanism.
   189  //
   190  // See GitHub API docs: https://developer.com/v3/oauth_authorizations/#check-an-authorization
   191  func getOAuthAppClient(t *testing.T) *github.Client {
   192  
   193  	username, ok := os.LookupEnv(envKeyClientID)
   194  	if !ok {
   195  		t.Skipf(msgEnvMissing, envKeyClientID)
   196  	}
   197  
   198  	password, ok := os.LookupEnv(envKeyClientSecret)
   199  	if !ok {
   200  		t.Skipf(msgEnvMissing, envKeyClientSecret)
   201  	}
   202  
   203  	tp := github.BasicAuthTransport{
   204  		Username: strings.TrimSpace(username),
   205  		Password: strings.TrimSpace(password),
   206  	}
   207  
   208  	return github.NewClient(tp.Client())
   209  }