Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cc23f20

Browse filesBrowse files
authored
Merge branch 'main' into implement_apps
2 parents 574f6a4 + 77e2d8f commit cc23f20
Copy full SHA for cc23f20

File tree

Expand file treeCollapse file tree

19 files changed

+332
-48
lines changed
Filter options
Expand file treeCollapse file tree

19 files changed

+332
-48
lines changed

‎.github/workflows/maven-build.yml

Copy file name to clipboardExpand all lines: .github/workflows/maven-build.yml
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
- name: Maven Install with Code Coverage
7777
run: mvn -B clean install -D enable-ci -Djapicmp.skip --file pom.xml
7878
- name: Codecov Report
79-
uses: codecov/codecov-action@v3.1.1
79+
uses: codecov/codecov-action@v3.1.3
8080
test:
8181
name: test (${{ matrix.os }}, Java ${{ matrix.java }})
8282
runs-on: ${{ matrix.os }}-latest

‎pom.xml

Copy file name to clipboardExpand all lines: pom.xml
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@
396396
<plugin>
397397
<groupId>com.github.siom79.japicmp</groupId>
398398
<artifactId>japicmp-maven-plugin</artifactId>
399-
<version>0.17.1</version>
399+
<version>0.17.2</version>
400400
<configuration>
401401
<parameter>
402402
<breakBuildOnBinaryIncompatibleModifications>true</breakBuildOnBinaryIncompatibleModifications>
+80Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package org.kohsuke.github.authorization;
2+
3+
import org.kohsuke.github.BetaApi;
4+
import org.kohsuke.github.GHApp;
5+
import org.kohsuke.github.GHAppInstallation;
6+
import org.kohsuke.github.GHAppInstallationToken;
7+
import org.kohsuke.github.GitHub;
8+
9+
import java.io.IOException;
10+
import java.time.Duration;
11+
import java.time.Instant;
12+
import java.util.Objects;
13+
14+
import javax.annotation.Nonnull;
15+
16+
/**
17+
* An AuthorizationProvider that performs automatic token refresh for an organization's AppInstallation.
18+
*/
19+
public class AppInstallationAuthorizationProvider extends GitHub.DependentAuthorizationProvider {
20+
21+
private final AppInstallationProvider appInstallationProvider;
22+
23+
private String authorization;
24+
25+
@Nonnull
26+
private Instant validUntil = Instant.MIN;
27+
28+
/**
29+
* Provides an AuthorizationProvider that performs automatic token refresh, based on an previously authenticated
30+
* github client.
31+
*
32+
* @param appInstallationProvider
33+
* An AppInstallationProvider that the authorization provider will use to retrieve the App.
34+
* @param authorizationProvider
35+
* A authorization provider that returns a JWT token that can be used to refresh the App Installation
36+
* token from GitHub.
37+
*/
38+
@BetaApi
39+
public AppInstallationAuthorizationProvider(AppInstallationProvider appInstallationProvider,
40+
AuthorizationProvider authorizationProvider) {
41+
super(authorizationProvider);
42+
this.appInstallationProvider = appInstallationProvider;
43+
}
44+
45+
@Override
46+
public String getEncodedAuthorization() throws IOException {
47+
synchronized (this) {
48+
if (authorization == null || Instant.now().isAfter(this.validUntil)) {
49+
String token = refreshToken();
50+
authorization = String.format("token %s", token);
51+
}
52+
return authorization;
53+
}
54+
}
55+
56+
private String refreshToken() throws IOException {
57+
GitHub gitHub = this.gitHub();
58+
GHAppInstallation installationByOrganization = appInstallationProvider.getAppInstallation(gitHub.getApp());
59+
GHAppInstallationToken ghAppInstallationToken = installationByOrganization.createToken().create();
60+
this.validUntil = ghAppInstallationToken.getExpiresAt().toInstant().minus(Duration.ofMinutes(5));
61+
return Objects.requireNonNull(ghAppInstallationToken.getToken());
62+
}
63+
64+
/**
65+
* Provides an interface that returns an app to be used by an AppInstallationAuthorizationProvider
66+
*/
67+
@FunctionalInterface
68+
public interface AppInstallationProvider {
69+
/**
70+
* Provides a GHAppInstallation for the given GHApp
71+
*
72+
* @param app
73+
* The GHApp to use
74+
* @return The GHAppInstallation
75+
* @throws IOException
76+
* on error
77+
*/
78+
GHAppInstallation getAppInstallation(GHApp app) throws IOException;
79+
}
80+
}
+6-40Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
11
package org.kohsuke.github.authorization;
22

33
import org.kohsuke.github.BetaApi;
4-
import org.kohsuke.github.GHAppInstallation;
5-
import org.kohsuke.github.GHAppInstallationToken;
6-
import org.kohsuke.github.GitHub;
7-
8-
import java.io.IOException;
9-
import java.time.Duration;
10-
import java.time.Instant;
11-
import java.util.Objects;
12-
13-
import javax.annotation.Nonnull;
144

155
/**
166
* An AuthorizationProvider that performs automatic token refresh for an organization's AppInstallation.
177
*/
18-
public class OrgAppInstallationAuthorizationProvider extends GitHub.DependentAuthorizationProvider {
19-
20-
private final String organizationName;
21-
22-
private String authorization;
23-
24-
@Nonnull
25-
private Instant validUntil = Instant.MIN;
8+
@Deprecated
9+
public class OrgAppInstallationAuthorizationProvider extends AppInstallationAuthorizationProvider {
2610

2711
/**
2812
* Provides an AuthorizationProvider that performs automatic token refresh, based on an previously authenticated
@@ -33,31 +17,13 @@ public class OrgAppInstallationAuthorizationProvider extends GitHub.DependentAut
3317
* @param authorizationProvider
3418
* A authorization provider that returns a JWT token that can be used to refresh the App Installation
3519
* token from GitHub.
20+
*
21+
* @deprecated Replaced by {@link AppInstallationAuthorizationProvider}
3622
*/
3723
@BetaApi
24+
@Deprecated
3825
public OrgAppInstallationAuthorizationProvider(String organizationName,
3926
AuthorizationProvider authorizationProvider) {
40-
super(authorizationProvider);
41-
this.organizationName = organizationName;
42-
}
43-
44-
@Override
45-
public String getEncodedAuthorization() throws IOException {
46-
synchronized (this) {
47-
if (authorization == null || Instant.now().isAfter(this.validUntil)) {
48-
String token = refreshToken();
49-
authorization = String.format("token %s", token);
50-
}
51-
return authorization;
52-
}
53-
}
54-
55-
private String refreshToken() throws IOException {
56-
GitHub gitHub = this.gitHub();
57-
GHAppInstallation installationByOrganization = gitHub.getApp()
58-
.getInstallationByOrganization(this.organizationName);
59-
GHAppInstallationToken ghAppInstallationToken = installationByOrganization.createToken().create();
60-
this.validUntil = ghAppInstallationToken.getExpiresAt().toInstant().minus(Duration.ofMinutes(5));
61-
return Objects.requireNonNull(ghAppInstallationToken.getToken());
27+
super(app -> app.getInstallationByOrganization(organizationName), authorizationProvider);
6228
}
6329
}

‎src/test/java/org/kohsuke/github/OrgAppInstallationAuthorizationProviderTest.java renamed to ‎src/test/java/org/kohsuke/github/AppInstallationAuthorizationProviderTest.java

Copy file name to clipboardExpand all lines: src/test/java/org/kohsuke/github/AppInstallationAuthorizationProviderTest.java
+31-4Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
package org.kohsuke.github;
22

33
import org.junit.Test;
4+
import org.kohsuke.github.authorization.AppInstallationAuthorizationProvider;
45
import org.kohsuke.github.authorization.ImmutableAuthorizationProvider;
56
import org.kohsuke.github.authorization.OrgAppInstallationAuthorizationProvider;
67

78
import java.io.IOException;
89

910
import static org.hamcrest.CoreMatchers.equalTo;
1011
import static org.hamcrest.CoreMatchers.notNullValue;
12+
import static org.hamcrest.CoreMatchers.startsWith;
1113

1214
// TODO: Auto-generated Javadoc
15+
1316
/**
14-
* The Class OrgAppInstallationAuthorizationProviderTest.
17+
* The Class AppInstallationAuthorizationProviderTest.
1518
*/
16-
public class OrgAppInstallationAuthorizationProviderTest extends AbstractGHAppInstallationTest {
19+
public class AppInstallationAuthorizationProviderTest extends AbstractGHAppInstallationTest {
1720

1821
/**
1922
* Instantiates a new org app installation authorization provider test.
2023
*/
21-
public OrgAppInstallationAuthorizationProviderTest() {
24+
public AppInstallationAuthorizationProviderTest() {
2225
useDefaultGitHub = false;
2326
}
2427

@@ -48,7 +51,8 @@ public void invalidJWTTokenRaisesException() throws IOException {
4851
*/
4952
@Test
5053
public void validJWTTokenAllowsOauthTokenRequest() throws IOException {
51-
OrgAppInstallationAuthorizationProvider provider = new OrgAppInstallationAuthorizationProvider("hub4j-test-org",
54+
AppInstallationAuthorizationProvider provider = new AppInstallationAuthorizationProvider(
55+
app -> app.getInstallationByOrganization("hub4j-test-org"),
5256
ImmutableAuthorizationProvider.fromJwtToken("bogus-valid-token"));
5357
gitHub = getGitHubBuilder().withAuthorizationProvider(provider)
5458
.withEndpoint(mockGitHub.apiServer().baseUrl())
@@ -59,4 +63,27 @@ public void validJWTTokenAllowsOauthTokenRequest() throws IOException {
5963
assertThat(encodedAuthorization, equalTo("token v1.9a12d913f980a45a16ac9c3a9d34d9b7sa314cb6"));
6064
}
6165

66+
/**
67+
* Lookup of an app by id works as expected
68+
*
69+
* @throws IOException
70+
* Signals that an I/O exception has occurred.
71+
*/
72+
@Test
73+
public void validJWTTokenWhenLookingUpAppById() throws IOException {
74+
AppInstallationAuthorizationProvider provider = new AppInstallationAuthorizationProvider(
75+
// https://github.com/organizations/hub4j-test-org/settings/installations/12129901
76+
app -> app.getInstallationById(12129901L),
77+
jwtProvider1);
78+
gitHub = getGitHubBuilder().withAuthorizationProvider(provider)
79+
.withEndpoint(mockGitHub.apiServer().baseUrl())
80+
.build();
81+
String encodedAuthorization = provider.getEncodedAuthorization();
82+
83+
assertThat(encodedAuthorization, notNullValue());
84+
// we could assert on the exact token with wiremock, but it would make the update of the test more complex
85+
// do we really care, getting a token should be enough.
86+
assertThat(encodedAuthorization, startsWith("token ghs_"));
87+
}
88+
6289
}

‎src/test/java/org/kohsuke/github/GHAuthenticatedAppInstallationTest.java

Copy file name to clipboardExpand all lines: src/test/java/org/kohsuke/github/GHAuthenticatedAppInstallationTest.java
+3-2Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.kohsuke.github;
22

33
import org.junit.Test;
4-
import org.kohsuke.github.authorization.OrgAppInstallationAuthorizationProvider;
4+
import org.kohsuke.github.authorization.AppInstallationAuthorizationProvider;
55

66
import java.io.IOException;
77
import java.util.List;
@@ -22,7 +22,8 @@ public class GHAuthenticatedAppInstallationTest extends AbstractGHAppInstallatio
2222
*/
2323
@Override
2424
protected GitHubBuilder getGitHubBuilder() {
25-
OrgAppInstallationAuthorizationProvider provider = new OrgAppInstallationAuthorizationProvider("hub4j-test-org",
25+
AppInstallationAuthorizationProvider provider = new AppInstallationAuthorizationProvider(
26+
app -> app.getInstallationByOrganization("hub4j-test-org"),
2627
jwtProvider1);
2728
return super.getGitHubBuilder().withAuthorizationProvider(provider);
2829
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"id": 82994,
3+
"slug": "ghapi-test-app-1",
4+
"node_id": "MDM6QXBwODI5OTQ=",
5+
"owner": {
6+
"login": "hub4j-test-org",
7+
"id": 7544739,
8+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
9+
"avatar_url": "https://avatars.githubusercontent.com/u/7544739?v=4",
10+
"gravatar_id": "",
11+
"url": "https://api.github.com/users/hub4j-test-org",
12+
"html_url": "https://github.com/hub4j-test-org",
13+
"followers_url": "https://api.github.com/users/hub4j-test-org/followers",
14+
"following_url": "https://api.github.com/users/hub4j-test-org/following{/other_user}",
15+
"gists_url": "https://api.github.com/users/hub4j-test-org/gists{/gist_id}",
16+
"starred_url": "https://api.github.com/users/hub4j-test-org/starred{/owner}{/repo}",
17+
"subscriptions_url": "https://api.github.com/users/hub4j-test-org/subscriptions",
18+
"organizations_url": "https://api.github.com/users/hub4j-test-org/orgs",
19+
"repos_url": "https://api.github.com/users/hub4j-test-org/repos",
20+
"events_url": "https://api.github.com/users/hub4j-test-org/events{/privacy}",
21+
"received_events_url": "https://api.github.com/users/hub4j-test-org/received_events",
22+
"type": "Organization",
23+
"site_admin": false
24+
},
25+
"name": "GHApi Test app 1",
26+
"description": "",
27+
"external_url": "http://localhost",
28+
"html_url": "https://github.com/apps/ghapi-test-app-1",
29+
"created_at": "2020-09-30T13:40:56Z",
30+
"updated_at": "2020-09-30T13:40:56Z",
31+
"permissions": {
32+
"contents": "read",
33+
"metadata": "read"
34+
},
35+
"events": [],
36+
"installations_count": 1
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"id": 12129901,
3+
"account": {
4+
"login": "hub4j-test-org",
5+
"id": 7544739,
6+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
7+
"avatar_url": "https://avatars.githubusercontent.com/u/7544739?v=4",
8+
"gravatar_id": "",
9+
"url": "https://api.github.com/users/hub4j-test-org",
10+
"html_url": "https://github.com/hub4j-test-org",
11+
"followers_url": "https://api.github.com/users/hub4j-test-org/followers",
12+
"following_url": "https://api.github.com/users/hub4j-test-org/following{/other_user}",
13+
"gists_url": "https://api.github.com/users/hub4j-test-org/gists{/gist_id}",
14+
"starred_url": "https://api.github.com/users/hub4j-test-org/starred{/owner}{/repo}",
15+
"subscriptions_url": "https://api.github.com/users/hub4j-test-org/subscriptions",
16+
"organizations_url": "https://api.github.com/users/hub4j-test-org/orgs",
17+
"repos_url": "https://api.github.com/users/hub4j-test-org/repos",
18+
"events_url": "https://api.github.com/users/hub4j-test-org/events{/privacy}",
19+
"received_events_url": "https://api.github.com/users/hub4j-test-org/received_events",
20+
"type": "Organization",
21+
"site_admin": false
22+
},
23+
"repository_selection": "selected",
24+
"access_tokens_url": "https://api.github.com/app/installations/12129901/access_tokens",
25+
"repositories_url": "https://api.github.com/installation/repositories",
26+
"html_url": "https://github.com/organizations/hub4j-test-org/settings/installations/12129901",
27+
"app_id": 82994,
28+
"app_slug": "ghapi-test-app-1",
29+
"target_id": 7544739,
30+
"target_type": "Organization",
31+
"permissions": {},
32+
"events": [],
33+
"created_at": "2020-09-30T13:41:32.000Z",
34+
"updated_at": "2023-03-21T14:39:11.000Z",
35+
"single_file_name": null,
36+
"has_multiple_single_files": false,
37+
"single_file_paths": [],
38+
"suspended_by": null,
39+
"suspended_at": null
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"id": "44e77d23-3e8f-4c80-a5fc-98546576386a",
3+
"name": "app",
4+
"request": {
5+
"url": "/app",
6+
"method": "GET",
7+
"headers": {
8+
"Accept": {
9+
"equalTo": "application/vnd.github.machine-man-preview+json"
10+
}
11+
}
12+
},
13+
"response": {
14+
"status": 200,
15+
"bodyFileName": "app-1.json",
16+
"headers": {
17+
"Server": "GitHub.com",
18+
"Date": "Fri, 14 Apr 2023 09:15:08 GMT",
19+
"Content-Type": "application/json; charset=utf-8",
20+
"Cache-Control": "public, max-age=60, s-maxage=60",
21+
"Vary": [
22+
"Accept",
23+
"Accept-Encoding, Accept, X-Requested-With"
24+
],
25+
"ETag": "W/\"662c8ea184a852f605e0c94a9789fe43965f939e16e02ff0b3a8cc1043078a62\"",
26+
"X-GitHub-Media-Type": "github.v3; param=machine-man-preview; format=json",
27+
"x-github-api-version-selected": "2022-11-28",
28+
"Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset",
29+
"Access-Control-Allow-Origin": "*",
30+
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
31+
"X-Frame-Options": "deny",
32+
"X-Content-Type-Options": "nosniff",
33+
"X-XSS-Protection": "0",
34+
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
35+
"Content-Security-Policy": "default-src 'none'",
36+
"X-GitHub-Request-Id": "D5E4:64DB:14B8E4B:2A5B09C:6439199C"
37+
}
38+
},
39+
"uuid": "44e77d23-3e8f-4c80-a5fc-98546576386a",
40+
"persistent": true,
41+
"insertionIndex": 1
42+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.