Skip to content

Commit

Permalink
Merge pull request #945 from MarcosCela/feat/credential-provider-refresh
Browse files Browse the repository at this point in the history
Feat/credential provider refresh
  • Loading branch information
bitwiseman committed Jan 14, 2021
2 parents 7e1531d + 1b84efd commit 3f99541
Show file tree
Hide file tree
Showing 26 changed files with 1,157 additions and 137 deletions.
13 changes: 7 additions & 6 deletions pom.xml
Expand Up @@ -45,6 +45,7 @@
<jacoco.coverage.target.class.method>0.25</jacoco.coverage.target.class.method>
<!-- For non-ci builds we'd like the build to still complete if jacoco metrics aren't met. -->
<jacoco.haltOnFailure>false</jacoco.haltOnFailure>
<jjwt.suite.version>0.11.2</jjwt.suite.version>
</properties>

<build>
Expand Down Expand Up @@ -489,20 +490,20 @@
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
<scope>test</scope>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>test</scope>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>test</scope>
<version>${jjwt.suite.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/kohsuke/github/GHEvent.java
Expand Up @@ -12,6 +12,7 @@
public enum GHEvent {
CHECK_RUN,
CHECK_SUITE,
CODE_SCANNING_ALERT,
COMMIT_COMMENT,
CONTENT_REFERENCE,
CREATE,
Expand Down
110 changes: 92 additions & 18 deletions src/main/java/org/kohsuke/github/GitHub.java
Expand Up @@ -26,6 +26,7 @@
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
import org.kohsuke.github.authorization.AuthorizationProvider;
import org.kohsuke.github.internal.Previews;

import java.io.*;
Expand Down Expand Up @@ -94,39 +95,112 @@ public class GitHub {
* "http://ghe.acme.com/api/v3". Note that GitHub Enterprise has <code>/api/v3</code> in the URL. For
* historical reasons, this parameter still accepts the bare domain name, but that's considered
* deprecated. Password is also considered deprecated as it is no longer required for api usage.
* @param login
* The user ID on GitHub that you are logging in as. Can be omitted if the OAuth token is provided or if
* logging in anonymously. Specifying this would save one API call.
* @param oauthAccessToken
* Secret OAuth token.
* @param password
* User's password. Always used in conjunction with the {@code login} parameter
* @param connector
* HttpConnector to use. Pass null to use default connector.
* a connector
* @param rateLimitHandler
* rateLimitHandler
* @param abuseLimitHandler
* abuseLimitHandler
* @param rateLimitChecker
* rateLimitChecker
* @param authorizationProvider
* a authorization provider
*/
GitHub(String apiUrl,
String login,
String oauthAccessToken,
String jwtToken,
String password,
HttpConnector connector,
RateLimitHandler rateLimitHandler,
AbuseLimitHandler abuseLimitHandler,
GitHubRateLimitChecker rateLimitChecker) throws IOException {
GitHubRateLimitChecker rateLimitChecker,
AuthorizationProvider authorizationProvider) throws IOException {
if (authorizationProvider instanceof DependentAuthorizationProvider) {
((DependentAuthorizationProvider) authorizationProvider).bind(this);
}

this.client = new GitHubHttpUrlConnectionClient(apiUrl,
login,
oauthAccessToken,
jwtToken,
password,
connector,
rateLimitHandler,
abuseLimitHandler,
rateLimitChecker,
(myself) -> setMyself(myself));
(myself) -> setMyself(myself),
authorizationProvider);
users = new ConcurrentHashMap<>();
orgs = new ConcurrentHashMap<>();
}

private GitHub(GitHubClient client) {
this.client = client;
users = new ConcurrentHashMap<>();
orgs = new ConcurrentHashMap<>();
}

public static abstract class DependentAuthorizationProvider implements AuthorizationProvider {

private GitHub baseGitHub;
private GitHub gitHub;
private final AuthorizationProvider authorizationProvider;

/**
* An AuthorizationProvider that requires an authenticated GitHub instance to provide its authorization.
*
* @param authorizationProvider
* A authorization provider to be used when refreshing this authorization provider.
*/
@BetaApi
@Deprecated
protected DependentAuthorizationProvider(AuthorizationProvider authorizationProvider) {
this.authorizationProvider = authorizationProvider;
}

/**
* Binds this authorization provider to a github instance.
*
* Only needs to be implemented by dynamic credentials providers that use a github instance in order to refresh.
*
* @param github
* The github instance to be used for refreshing dynamic credentials
*/
synchronized void bind(GitHub github) {
if (baseGitHub != null) {
throw new IllegalStateException("Already bound to another GitHub instance.");
}
this.baseGitHub = github;
}

protected synchronized final GitHub gitHub() {
if (gitHub == null) {
gitHub = new GitHub.AuthorizationRefreshGitHubWrapper(this.baseGitHub, authorizationProvider);
}
return gitHub;
}
}

private static class AuthorizationRefreshGitHubWrapper extends GitHub {

private final AuthorizationProvider authorizationProvider;

AuthorizationRefreshGitHubWrapper(GitHub github, AuthorizationProvider authorizationProvider) {
super(github.client);
this.authorizationProvider = authorizationProvider;

// no dependent authorization providers nest like this currently, but they might in future
if (authorizationProvider instanceof DependentAuthorizationProvider) {
((DependentAuthorizationProvider) authorizationProvider).bind(this);
}
}

@Nonnull
@Override
Requester createRequest() {
try {
// Override
return super.createRequest().setHeader("Authorization", authorizationProvider.getEncodedAuthorization())
.rateLimit(RateLimitTarget.NONE);
} catch (IOException e) {
throw new GHException("Failed to create requester to refresh credentials", e);
}
}
}

/**
* Obtains the credential from "~/.github" or from the System Environment Properties.
*
Expand Down
63 changes: 40 additions & 23 deletions src/main/java/org/kohsuke/github/GitHubBuilder.java
@@ -1,6 +1,8 @@
package org.kohsuke.github;

import org.apache.commons.io.IOUtils;
import org.kohsuke.github.authorization.AuthorizationProvider;
import org.kohsuke.github.authorization.ImmutableAuthorizationProvider;
import org.kohsuke.github.extras.ImpatientHttpConnector;

import java.io.File;
Expand All @@ -24,16 +26,13 @@ public class GitHubBuilder implements Cloneable {

// default scoped so unit tests can read them.
/* private */ String endpoint = GitHubClient.GITHUB_URL;
/* private */ String user;
/* private */ String password;
/* private */ String oauthToken;
/* private */ String jwtToken;

private HttpConnector connector;

private RateLimitHandler rateLimitHandler = RateLimitHandler.WAIT;
private AbuseLimitHandler abuseLimitHandler = AbuseLimitHandler.WAIT;
private GitHubRateLimitChecker rateLimitChecker = new GitHubRateLimitChecker();
/* private */ AuthorizationProvider authorizationProvider = AuthorizationProvider.ANONYMOUS;

/**
* Instantiates a new Git hub builder.
Expand Down Expand Up @@ -61,13 +60,13 @@ static GitHubBuilder fromCredentials() throws IOException {

builder = fromEnvironment();

if (builder.oauthToken != null || builder.user != null || builder.jwtToken != null)
if (builder.authorizationProvider != null)
return builder;

try {
builder = fromPropertyFile();

if (builder.oauthToken != null || builder.user != null || builder.jwtToken != null)
if (builder.authorizationProvider != null)
return builder;
} catch (FileNotFoundException e) {
// fall through
Expand Down Expand Up @@ -215,9 +214,20 @@ public static GitHubBuilder fromPropertyFile(String propertyFileName) throws IOE
*/
public static GitHubBuilder fromProperties(Properties props) {
GitHubBuilder self = new GitHubBuilder();
self.withOAuthToken(props.getProperty("oauth"), props.getProperty("login"));
self.withJwtToken(props.getProperty("jwt"));
self.withPassword(props.getProperty("login"), props.getProperty("password"));
String oauth = props.getProperty("oauth");
String jwt = props.getProperty("jwt");
String login = props.getProperty("login");
String password = props.getProperty("password");

if (oauth != null) {
self.withOAuthToken(oauth, login);
}
if (jwt != null) {
self.withJwtToken(jwt);
}
if (password != null) {
self.withPassword(login, password);
}
self.withEndpoint(props.getProperty("endpoint", GitHubClient.GITHUB_URL));
return self;
}
Expand Down Expand Up @@ -247,9 +257,7 @@ public GitHubBuilder withEndpoint(String endpoint) {
* @return the git hub builder
*/
public GitHubBuilder withPassword(String user, String password) {
this.user = user;
this.password = password;
return this;
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromLoginAndPassword(user, password));
}

/**
Expand All @@ -260,7 +268,7 @@ public GitHubBuilder withPassword(String user, String password) {
* @return the git hub builder
*/
public GitHubBuilder withOAuthToken(String oauthToken) {
return withOAuthToken(oauthToken, null);
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromOauthToken(oauthToken));
}

/**
Expand All @@ -273,8 +281,21 @@ public GitHubBuilder withOAuthToken(String oauthToken) {
* @return the git hub builder
*/
public GitHubBuilder withOAuthToken(String oauthToken, String user) {
this.oauthToken = oauthToken;
this.user = user;
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromOauthToken(oauthToken, user));
}

/**
* Configures a {@link AuthorizationProvider} for this builder
*
* There can be only one authorization provider per client instance.
*
* @param authorizationProvider
* the authorization provider
* @return the git hub builder
*
*/
public GitHubBuilder withAuthorizationProvider(final AuthorizationProvider authorizationProvider) {
this.authorizationProvider = authorizationProvider;
return this;
}

Expand All @@ -287,7 +308,7 @@ public GitHubBuilder withOAuthToken(String oauthToken, String user) {
* @see GHAppInstallation#createToken(java.util.Map) GHAppInstallation#createToken(java.util.Map)
*/
public GitHubBuilder withAppInstallationToken(String appInstallationToken) {
return withOAuthToken(appInstallationToken, "");
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromAppInstallationToken(appInstallationToken));
}

/**
Expand All @@ -298,8 +319,7 @@ public GitHubBuilder withAppInstallationToken(String appInstallationToken) {
* @return the git hub builder
*/
public GitHubBuilder withJwtToken(String jwtToken) {
this.jwtToken = jwtToken;
return this;
return withAuthorizationProvider(ImmutableAuthorizationProvider.fromJwtToken(jwtToken));
}

/**
Expand Down Expand Up @@ -421,14 +441,11 @@ public GitHubBuilder withProxy(final Proxy p) {
*/
public GitHub build() throws IOException {
return new GitHub(endpoint,
user,
oauthToken,
jwtToken,
password,
connector,
rateLimitHandler,
abuseLimitHandler,
rateLimitChecker);
rateLimitChecker,
authorizationProvider);
}

@Override
Expand Down

0 comments on commit 3f99541

Please sign in to comment.