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