Skip to content

Commit 5fe2817

Browse files
authored
Merge pull request #764 from bitwiseman/issue/valid_creds
Change credential check to use rate limit
2 parents b337bb3 + 65ae41c commit 5fe2817

File tree

11 files changed

+331
-21
lines changed

11 files changed

+331
-21
lines changed

src/main/java/org/kohsuke/github/GitHubClient.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,13 +148,15 @@ private <T> T fetch(Class<T> type, String urlPath) throws IOException {
148148
}
149149

150150
/**
151-
* Ensures that the credential is valid.
151+
* Ensures that the credential for this client is valid.
152152
*
153153
* @return the boolean
154154
*/
155155
public boolean isCredentialValid() {
156156
try {
157-
fetch(GHUser.class, "/user");
157+
// If 404, ratelimit returns a default value.
158+
// This works as credential test because invalid credentials returns 401, not 404
159+
getRateLimit();
158160
return true;
159161
} catch (IOException e) {
160162
if (LOGGER.isLoggable(FINE))

src/test/java/org/kohsuke/github/AppTest.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,34 @@ private void cleanupUserRepository(final String name) throws IOException {
7777
@Test
7878
public void testCredentialValid() throws IOException {
7979
assertTrue(gitHub.isCredentialValid());
80-
GitHub connect = GitHub.connect("totally", "bogus");
81-
assertFalse(connect.isCredentialValid());
80+
assertThat(gitHub.lastRateLimit().getCore(), not(instanceOf(GHRateLimit.UnknownLimitRecord.class)));
81+
assertThat(gitHub.lastRateLimit().getCore().getLimit(), equalTo(5000));
82+
83+
gitHub = getGitHubBuilder().withOAuthToken("bogus", "user")
84+
.withEndpoint(mockGitHub.apiServer().baseUrl())
85+
.build();
86+
assertThat(gitHub.lastRateLimit(), nullValue());
87+
assertFalse(gitHub.isCredentialValid());
88+
// For invalid credentials, we get a 401 but it includes anonymous rate limit headers
89+
assertThat(gitHub.lastRateLimit().getCore(), not(instanceOf(GHRateLimit.UnknownLimitRecord.class)));
90+
assertThat(gitHub.lastRateLimit().getCore().getLimit(), equalTo(60));
91+
}
92+
93+
@Test
94+
public void testCredentialValidEnterprise() throws IOException {
95+
// Simulated GHE: getRateLimit returns 404
96+
assertThat(gitHub.lastRateLimit(), nullValue());
97+
assertTrue(gitHub.isCredentialValid());
98+
// lastRateLimit stays null when 404 is encountered
99+
assertThat(gitHub.lastRateLimit(), nullValue());
100+
101+
gitHub = getGitHubBuilder().withOAuthToken("bogus", "user")
102+
.withEndpoint(mockGitHub.apiServer().baseUrl())
103+
.build();
104+
assertThat(gitHub.lastRateLimit(), nullValue());
105+
assertFalse(gitHub.isCredentialValid());
106+
// Simulated GHE: For invalid credentials, we get a 401 that does not include ratelimit info
107+
assertThat(gitHub.lastRateLimit(), nullValue());
82108
}
83109

84110
@Test
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"resources": {
3+
"core": {
4+
"limit": 5000,
5+
"remaining": 4994,
6+
"reset": 1585671841
7+
},
8+
"search": {
9+
"limit": 30,
10+
"remaining": 30,
11+
"reset": 1585671895
12+
},
13+
"graphql": {
14+
"limit": 5000,
15+
"remaining": 5000,
16+
"reset": 1585675435
17+
},
18+
"integration_manifest": {
19+
"limit": 5000,
20+
"remaining": 5000,
21+
"reset": 1585675435
22+
},
23+
"source_import": {
24+
"limit": 100,
25+
"remaining": 100,
26+
"reset": 1585671895
27+
}
28+
},
29+
"rate": {
30+
"limit": 5000,
31+
"remaining": 4994,
32+
"reset": 1585671841
33+
}
34+
}

src/test/resources/org/kohsuke/github/AppTest/wiremock/testCredentialValid/__files/user-1.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@
2424
"email": "[email protected]",
2525
"hireable": null,
2626
"bio": "https://twitter.com/bitwiseman",
27-
"public_repos": 167,
28-
"public_gists": 4,
29-
"followers": 136,
27+
"public_repos": 181,
28+
"public_gists": 7,
29+
"followers": 151,
3030
"following": 9,
3131
"created_at": "2012-07-11T20:38:33Z",
32-
"updated_at": "2019-09-24T19:32:29Z",
33-
"private_gists": 7,
34-
"total_private_repos": 9,
32+
"updated_at": "2020-03-27T19:14:56Z",
33+
"private_gists": 8,
34+
"total_private_repos": 10,
3535
"owned_private_repos": 0,
3636
"disk_usage": 33697,
3737
"collaborators": 0,
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"id": "c875f18e-a0af-4114-8d1f-123b5377a52c",
3+
"name": "rate_limit",
4+
"request": {
5+
"url": "/rate_limit",
6+
"method": "GET",
7+
"headers": {
8+
"Accept": {
9+
"equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
10+
}
11+
}
12+
},
13+
"response": {
14+
"status": 200,
15+
"bodyFileName": "rate_limit-2.json",
16+
"headers": {
17+
"Date": "Tue, 31 Mar 2020 16:23:55 GMT",
18+
"Content-Type": "application/json; charset=utf-8",
19+
"Server": "GitHub.com",
20+
"Status": "200 OK",
21+
"X-RateLimit-Limit": "5000",
22+
"X-RateLimit-Remaining": "4994",
23+
"X-RateLimit-Reset": "1585671841",
24+
"Cache-Control": "no-cache",
25+
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
26+
"X-Accepted-OAuth-Scopes": "",
27+
"X-GitHub-Media-Type": "unknown, github.v3",
28+
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
29+
"X-Frame-Options": "deny",
30+
"X-Content-Type-Options": "nosniff",
31+
"X-XSS-Protection": "1; mode=block",
32+
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
33+
"Content-Security-Policy": "default-src 'none'",
34+
"Vary": "Accept-Encoding, Accept, X-Requested-With",
35+
"X-GitHub-Request-Id": "C14E:8628:147DC:18E26:5E836E9A"
36+
}
37+
},
38+
"uuid": "c875f18e-a0af-4114-8d1f-123b5377a52c",
39+
"persistent": true,
40+
"scenarioName": "scenario-1-rate_limit",
41+
"requiredScenarioState": "Started",
42+
"newScenarioState": "scenario-1-rate_limit-2",
43+
"insertionIndex": 2
44+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"id": "5ba21199-9f1d-420c-87e9-ab30d3e73884",
3+
"name": "rate_limit",
4+
"request": {
5+
"url": "/rate_limit",
6+
"method": "GET",
7+
"headers": {
8+
"Accept": {
9+
"equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
10+
}
11+
}
12+
},
13+
"response": {
14+
"status": 401,
15+
"body": "{\"message\":\"Bad credentials\",\"documentation_url\":\"https://developer.github.com/v3\"}",
16+
"headers": {
17+
"Date": "Tue, 31 Mar 2020 16:23:55 GMT",
18+
"Content-Type": "application/json; charset=utf-8",
19+
"Server": "GitHub.com",
20+
"Status": "401 Unauthorized",
21+
"X-GitHub-Media-Type": "unknown, github.v3",
22+
"X-RateLimit-Limit": "60",
23+
"X-RateLimit-Remaining": "59",
24+
"X-RateLimit-Reset": "1585675435",
25+
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
26+
"X-Frame-Options": "deny",
27+
"X-Content-Type-Options": "nosniff",
28+
"X-XSS-Protection": "1; mode=block",
29+
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
30+
"Content-Security-Policy": "default-src 'none'",
31+
"Vary": "Accept-Encoding, Accept, X-Requested-With",
32+
"X-GitHub-Request-Id": "C14E:8628:147E6:18E5B:5E836E9B"
33+
}
34+
},
35+
"uuid": "5ba21199-9f1d-420c-87e9-ab30d3e73884",
36+
"persistent": true,
37+
"scenarioName": "scenario-1-rate_limit",
38+
"requiredScenarioState": "scenario-1-rate_limit-2",
39+
"insertionIndex": 3
40+
}
Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"id": "eba96b24-3354-4d21-bd2a-388a13ba9832",
2+
"id": "27891e8f-aae7-459b-9860-a4f505cec688",
33
"name": "user",
44
"request": {
55
"url": "/user",
@@ -14,35 +14,33 @@
1414
"status": 200,
1515
"bodyFileName": "user-1.json",
1616
"headers": {
17-
"Date": "Wed, 02 Oct 2019 21:39:57 GMT",
17+
"Date": "Tue, 31 Mar 2020 16:23:54 GMT",
1818
"Content-Type": "application/json; charset=utf-8",
1919
"Server": "GitHub.com",
2020
"Status": "200 OK",
2121
"X-RateLimit-Limit": "5000",
22-
"X-RateLimit-Remaining": "4964",
23-
"X-RateLimit-Reset": "1570055937",
22+
"X-RateLimit-Remaining": "4995",
23+
"X-RateLimit-Reset": "1585671841",
2424
"Cache-Control": "private, max-age=60, s-maxage=60",
2525
"Vary": [
2626
"Accept, Authorization, Cookie, X-GitHub-OTP",
27-
"Accept-Encoding"
27+
"Accept-Encoding, Accept, X-Requested-With"
2828
],
29-
"ETag": "W/\"cf6199fecf47b59c42190e1e11147ee2\"",
30-
"Last-Modified": "Tue, 24 Sep 2019 19:32:29 GMT",
29+
"ETag": "W/\"740bb7db37d5437d08ea1aa5e652cf37\"",
30+
"Last-Modified": "Fri, 27 Mar 2020 19:14:56 GMT",
3131
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
3232
"X-Accepted-OAuth-Scopes": "",
3333
"X-GitHub-Media-Type": "unknown, github.v3",
34-
"Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type",
35-
"Access-Control-Allow-Origin": "*",
3634
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
3735
"X-Frame-Options": "deny",
3836
"X-Content-Type-Options": "nosniff",
3937
"X-XSS-Protection": "1; mode=block",
4038
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
4139
"Content-Security-Policy": "default-src 'none'",
42-
"X-GitHub-Request-Id": "C2BA:67D6:CA2979:EFEEDC:5D95192D"
40+
"X-GitHub-Request-Id": "C14E:8628:147B0:18E1A:5E836E9A"
4341
}
4442
},
45-
"uuid": "eba96b24-3354-4d21-bd2a-388a13ba9832",
43+
"uuid": "27891e8f-aae7-459b-9860-a4f505cec688",
4644
"persistent": true,
4745
"insertionIndex": 1
4846
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"login": "bitwiseman",
3+
"id": 1958953,
4+
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
5+
"avatar_url": "https://avatars3.githubusercontent.com/u/1958953?v=4",
6+
"gravatar_id": "",
7+
"url": "https://api.github.com/users/bitwiseman",
8+
"html_url": "https://github.com/bitwiseman",
9+
"followers_url": "https://api.github.com/users/bitwiseman/followers",
10+
"following_url": "https://api.github.com/users/bitwiseman/following{/other_user}",
11+
"gists_url": "https://api.github.com/users/bitwiseman/gists{/gist_id}",
12+
"starred_url": "https://api.github.com/users/bitwiseman/starred{/owner}{/repo}",
13+
"subscriptions_url": "https://api.github.com/users/bitwiseman/subscriptions",
14+
"organizations_url": "https://api.github.com/users/bitwiseman/orgs",
15+
"repos_url": "https://api.github.com/users/bitwiseman/repos",
16+
"events_url": "https://api.github.com/users/bitwiseman/events{/privacy}",
17+
"received_events_url": "https://api.github.com/users/bitwiseman/received_events",
18+
"type": "User",
19+
"site_admin": false,
20+
"name": "Liam Newman",
21+
"company": "Cloudbees, Inc.",
22+
"blog": "",
23+
"location": "Seattle, WA, USA",
24+
"email": "[email protected]",
25+
"hireable": null,
26+
"bio": "https://twitter.com/bitwiseman",
27+
"public_repos": 181,
28+
"public_gists": 7,
29+
"followers": 151,
30+
"following": 9,
31+
"created_at": "2012-07-11T20:38:33Z",
32+
"updated_at": "2020-03-27T19:14:56Z",
33+
"private_gists": 8,
34+
"total_private_repos": 10,
35+
"owned_private_repos": 0,
36+
"disk_usage": 33697,
37+
"collaborators": 0,
38+
"two_factor_authentication": true,
39+
"plan": {
40+
"name": "free",
41+
"space": 976562499,
42+
"collaborators": 0,
43+
"private_repos": 10000
44+
}
45+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"id": "c875f18e-a0af-4114-8d1f-123b5377a52c",
3+
"name": "rate_limit",
4+
"request": {
5+
"url": "/rate_limit",
6+
"method": "GET",
7+
"headers": {
8+
"Accept": {
9+
"equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
10+
}
11+
}
12+
},
13+
"response": {
14+
"status": 404,
15+
"body": "{\"message\":\"Not Found\",\"documentation_url\":\"https://developer.github.com/v3\"}",
16+
"headers": {
17+
"Date": "Tue, 31 Mar 2020 16:23:55 GMT",
18+
"Content-Type": "application/json; charset=utf-8",
19+
"Server": "GitHub.com",
20+
"Status": "404 Not Found",
21+
"Cache-Control": "no-cache",
22+
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
23+
"X-Accepted-OAuth-Scopes": "",
24+
"X-GitHub-Media-Type": "unknown, github.v3",
25+
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
26+
"X-Frame-Options": "deny",
27+
"X-Content-Type-Options": "nosniff",
28+
"X-XSS-Protection": "1; mode=block",
29+
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
30+
"Content-Security-Policy": "default-src 'none'",
31+
"Vary": "Accept-Encoding, Accept, X-Requested-With",
32+
"X-GitHub-Request-Id": "C14E:8628:147DC:18E26:5E836E9A"
33+
}
34+
},
35+
"uuid": "c875f18e-a0af-4114-8d1f-123b5377a52c",
36+
"persistent": true,
37+
"scenarioName": "scenario-1-rate_limit",
38+
"requiredScenarioState": "Started",
39+
"newScenarioState": "scenario-1-rate_limit-2",
40+
"insertionIndex": 2
41+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"id": "5ba21199-9f1d-420c-87e9-ab30d3e73884",
3+
"name": "rate_limit",
4+
"request": {
5+
"url": "/rate_limit",
6+
"method": "GET",
7+
"headers": {
8+
"Accept": {
9+
"equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
10+
}
11+
}
12+
},
13+
"response": {
14+
"status": 401,
15+
"body": "{\"message\":\"Bad credentials\",\"documentation_url\":\"https://developer.github.com/v3\"}",
16+
"headers": {
17+
"Date": "Tue, 31 Mar 2020 16:23:55 GMT",
18+
"Content-Type": "application/json; charset=utf-8",
19+
"Server": "GitHub.com",
20+
"Status": "401 Unauthorized",
21+
"X-GitHub-Media-Type": "unknown, github.v3",
22+
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
23+
"X-Frame-Options": "deny",
24+
"X-Content-Type-Options": "nosniff",
25+
"X-XSS-Protection": "1; mode=block",
26+
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
27+
"Content-Security-Policy": "default-src 'none'",
28+
"Vary": "Accept-Encoding, Accept, X-Requested-With",
29+
"X-GitHub-Request-Id": "C14E:8628:147E6:18E5B:5E836E9B"
30+
}
31+
},
32+
"uuid": "5ba21199-9f1d-420c-87e9-ab30d3e73884",
33+
"persistent": true,
34+
"scenarioName": "scenario-1-rate_limit",
35+
"requiredScenarioState": "scenario-1-rate_limit-2",
36+
"insertionIndex": 3
37+
}

0 commit comments

Comments
 (0)