From 318b60a1f2e2f9a44a4c605c2407cf9ca0dc2d55 Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Tue, 24 Mar 2020 14:48:34 +0000 Subject: [PATCH 1/4] Support multiple orgs / users --- docs/github-app.adoc | 3 +- .../GitHubAppCredentials.java | 58 +++++++++++++++---- .../GitHubAppCredentials/config.jelly | 8 ++- .../GitHubAppCredentials/help-owner.html | 3 + 4 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html diff --git a/docs/github-app.adoc b/docs/github-app.adoc index 3ac990e9e..126249d75 100644 --- a/docs/github-app.adoc +++ b/docs/github-app.adoc @@ -87,7 +87,8 @@ Fill out the form: - App ID: the github app ID, it can be found in the 'About' section of your GitHub app in the general tab. - API endpoint (optional, only required for GitHub enterprise this will only show up if a GitHub enterprise server is configured). - Key: click add, paste the contents of the converted private key -- Passphrase: do not fill this field, it will be ignored +- Advanced: (optional) If you've installed your same GitHub app on multiple organisations you need the next step + * Owner: the name of the organisation or user, i.e. jenkinsci for https://github.com/jenkinsci - Click OK === link:https://github.com/jenkinsci/configuration-as-code-plugin[Configuration as Code Plugin] diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java index 4ff804e2c..6b570ff21 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java @@ -30,6 +30,9 @@ public class GitHubAppCredentials extends BaseStandardCredentials implements StandardUsernamePasswordCredentials { private static final String ERROR_AUTHENTICATING_GITHUB_APP = "Couldn't authenticate with GitHub app ID %s"; + private static final String NOT_INSTALLED = ", has it been installed to your GitHub organisation / user?"; + + private static final String ERROR_NOT_INSTALLED = ERROR_AUTHENTICATING_GITHUB_APP + NOT_INSTALLED; @NonNull private final String appID; @@ -39,6 +42,8 @@ public class GitHubAppCredentials extends BaseStandardCredentials implements Sta private String apiUri; + private String owner; + @DataBoundConstructor @SuppressWarnings("unused") // by stapler public GitHubAppCredentials( @@ -72,8 +77,26 @@ public Secret getPrivateKey() { return privateKey; } + /** + * Owner of this installation, i.e. a user or organisation, + * used to differeniate app installations when the app is installed to multiple organisations / users. + * + * If this is null then call listInstallations and if there's only one in the list then use that installation. + * + * @return the owner of the organisation or null. + */ + @CheckForNull + public String getOwner() { + return owner; + } + + @DataBoundSetter + public void setOwner(String owner) { + this.owner = owner; + } + @SuppressWarnings("deprecation") // preview features are required for GitHub app integration, GitHub api adds deprecated to all preview methods - static String generateAppInstallationToken(String appId, String appPrivateKey, String apiUrl) { + static String generateAppInstallationToken(String appId, String appPrivateKey, String apiUrl, String owner) { try { String jwtToken = createJWT(appId, appPrivateKey); GitHub gitHubApp = new GitHubBuilder().withEndpoint(apiUrl).withJwtToken(jwtToken).build(); @@ -81,18 +104,28 @@ static String generateAppInstallationToken(String appId, String appPrivateKey, S GHApp app = gitHubApp.getApp(); List appInstallations = app.listInstallations().asList(); - if (!appInstallations.isEmpty()) { - GHAppInstallation appInstallation = appInstallations.get(0); - GHAppInstallationToken appInstallationToken = appInstallation - .createToken(appInstallation.getPermissions()) - .create(); - - return appInstallationToken.getToken(); + if (appInstallations.isEmpty()) { + throw new IllegalArgumentException(String.format(ERROR_NOT_INSTALLED, appId)); } + GHAppInstallation appInstallation; + if (appInstallations.size() == 1) { + appInstallation = appInstallations.get(0); + } else { + appInstallation = appInstallations.stream() + .filter(installation -> installation.getAccount().getLogin().equals(owner)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(String.format(ERROR_NOT_INSTALLED, appId))); + } + + GHAppInstallationToken appInstallationToken = appInstallation + .createToken(appInstallation.getPermissions()) + .create(); + + return appInstallationToken.getToken(); } catch (IOException e) { throw new IllegalArgumentException(String.format(ERROR_AUTHENTICATING_GITHUB_APP, appId), e); } - throw new IllegalArgumentException(String.format(ERROR_AUTHENTICATING_GITHUB_APP, appId)); + } /** @@ -105,7 +138,7 @@ public Secret getPassword() { apiUri = "https://api.github.com"; } - String appInstallationToken = generateAppInstallationToken(appID, privateKey.getPlainText(), apiUri); + String appInstallationToken = generateAppInstallationToken(appID, privateKey.getPlainText(), apiUri, owner); return Secret.fromString(appInstallationToken); } @@ -163,7 +196,8 @@ public ListBoxModel doFillApiUriItems() { public FormValidation doTestConnection( @QueryParameter("appID") final String appID, @QueryParameter("privateKey") final String privateKey, - @QueryParameter("apiUri") final String apiUri + @QueryParameter("apiUri") final String apiUri, + @QueryParameter("owner") final String owner ) { GitHubAppCredentials gitHubAppCredential = new GitHubAppCredentials( @@ -171,10 +205,10 @@ public FormValidation doTestConnection( appID, Secret.fromString(privateKey) ); gitHubAppCredential.setApiUri(apiUri); + gitHubAppCredential.setOwner(owner); try { GitHub connect = Connector.connect(apiUri, gitHubAppCredential); - return FormValidation.ok("Success, Remaining rate limit: " + connect.getRateLimit().getRemaining()); } catch (Exception e) { return FormValidation.error(e, String.format(ERROR_AUTHENTICATING_GITHUB_APP, appID)); diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/config.jelly b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/config.jelly index 5efc33569..a6fcfeaeb 100644 --- a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/config.jelly +++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/config.jelly @@ -24,7 +24,13 @@ + + + + + + + method="testConnection" with="appID,apiUri,privateKey,owner" /> diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html new file mode 100644 index 000000000..0c080680c --- /dev/null +++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html @@ -0,0 +1,3 @@ +

+ The organisation or user that this app is to be used for, only required if this app is installed to multiple organisations. +

From f7ad93338c7e840a28267a8b6f499de20c06ee70 Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Tue, 24 Mar 2020 22:14:52 +0000 Subject: [PATCH 2/4] Update docs/github-app.adoc Co-Authored-By: Liam Newman --- docs/github-app.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/github-app.adoc b/docs/github-app.adoc index 126249d75..9c31da02e 100644 --- a/docs/github-app.adoc +++ b/docs/github-app.adoc @@ -87,7 +87,7 @@ Fill out the form: - App ID: the github app ID, it can be found in the 'About' section of your GitHub app in the general tab. - API endpoint (optional, only required for GitHub enterprise this will only show up if a GitHub enterprise server is configured). - Key: click add, paste the contents of the converted private key -- Advanced: (optional) If you've installed your same GitHub app on multiple organisations you need the next step +- Advanced: (optional) If you've installed your same GitHub app on multiple organizations you need the next step * Owner: the name of the organisation or user, i.e. jenkinsci for https://github.com/jenkinsci - Click OK From 83f1d743cd736229af19802dfcf68efb62b4dd08 Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Tue, 31 Mar 2020 19:29:28 +0100 Subject: [PATCH 3/4] Update src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java Co-Authored-By: Jesse Glick --- .../plugins/github_branch_source/GitHubAppCredentials.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java index 6b570ff21..c4a152bb4 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java @@ -92,7 +92,7 @@ public String getOwner() { @DataBoundSetter public void setOwner(String owner) { - this.owner = owner; + this.owner = Util.fixEmpty(owner); } @SuppressWarnings("deprecation") // preview features are required for GitHub app integration, GitHub api adds deprecated to all preview methods From aee2719c6b139c485269e9d99fde1fa363c5a93f Mon Sep 17 00:00:00 2001 From: Tim Jacomb Date: Tue, 31 Mar 2020 19:29:37 +0100 Subject: [PATCH 4/4] Update src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html Co-Authored-By: Jesse Glick --- .../github_branch_source/GitHubAppCredentials/help-owner.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html index 0c080680c..2097e42f0 100644 --- a/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html +++ b/src/main/resources/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials/help-owner.html @@ -1,3 +1,3 @@

- The organisation or user that this app is to be used for, only required if this app is installed to multiple organisations. + The organisation or user that this app is to be used for. Only required if this app is installed to multiple organisations.