Skip to content

Commit

Permalink
[JENKINS-73474] Add GitHubAppCredentials.withOwner non regression t…
Browse files Browse the repository at this point in the history
…est (#804)

* [JENKINS-73474] Add withOwner non regression test

* [JENKINS-73474] Add wiremock test

* Trigger Build

* [JENKINS-73474] Cleanup

* [JENKINS-73474] Reorganize test resources

* [JENKINS-73474] Reorganize test resources

* [JENKINS-73474] Cleanup

* Trigger Build

* [JENKINS-73474] Apply suggestions from the review

* [JENKINS-73474] Add issue annotation
  • Loading branch information
jeromepochat authored Oct 8, 2024
1 parent c406746 commit 11ce4fe
Show file tree
Hide file tree
Showing 7 changed files with 522 additions and 45 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.jenkinsci.plugins.github_branch_source;

import com.cloudbees.plugins.credentials.CredentialsScope;
import hudson.util.Secret;

public class GitHubApp {

// PKCS8 private key (https://stackoverflow.com/a/22176759/4951015)
private static final String PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n"
+
// Windows line ending
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD7vHsVwyDV8cj7\r\n"
+
// This should also work
"5yR4WWl6rlgf/e5zmeBgtm0PCgnitcSbD5FU33301DPY5a7AtqVBOwEnE14L9XS7\r"
+ "ov61U+x1m4aQmqR/dPQaA2ayh2cYPszWNQMp42ArDIfg7DhSrvsRJKHsbPXlPjqe\n"
+ "c0udLqhSLVIO9frNLf+dAsLsgYk8O39PKGb33akGG7tWTe0J+akNQjgbS7vOi8sS\n"
+ "NLwHIdYfz/Am+6Xmm+J4yVs6+Xt3kOeLdFBkz8H/HGsJq854MbIAK/HuId1MOPS0\n"
+ "cDWh37tzRsM+q/HZzYRkc5bhNKw/Mj9jN9jD5GH0Lfea0QFedjppf1KvWdcXn+/W\n"
+ "M7OmyfhvAgMBAAECggEAN96H7reExRbJRWbySCeH6mthMZB46H0hODWklK7krMUs\n"
+ "okFdPtnvKXQjIaMwGqMuoACJa/O3bq4GP1KYdwPuOdfPkK5RjdwWBOP2We8FKXNe\n"
+ "oLfZQOWuxT8dtQSYJ3mgTRi1OzSfikY6Wko6YOMnBj36tUlQZVMtJNqlCjphi9Uz\n"
+ "6EyvRURlDG8sBBbC7ods5B0789qk3iGH/97ia+1QIqXAUaVFg3/BA6wkxkbNG2sN\n"
+ "tqULgVYTw32Oj/Y/H1Y250RoocTyfsUS3I3aPIlnvcgp2bugWqDyYJ58nDIt3Pku\n"
+ "fjImWrNz/pNiEs+efnb0QEk7m5hYwxmyXN4KRSv0OQKBgQD+I3Y3iNKSVr6wXjur\n"
+ "OPp45fxS2sEf5FyFYOn3u760sdJOH9fGlmf9sDozJ8Y8KCaQCN5tSe3OM+XDrmiw\n"
+ "Cu/oaqJ1+G4RG+6w1RJF+5Nfg6PkUs7eJehUgZ2Tox8Tg1mfVIV8KbMwNi5tXpug\n"
+ "MVmA2k9xjc4uMd2jSnSj9NAqrQKBgQD9lIO1tY6YKF0Eb0Qi/iLN4UqBdJfnALBR\n"
+ "MjxYxqqI8G4wZEoZEJJvT1Lm6Q3o577N95SihZoj69tb10vvbEz1pb3df7c1HEku\n"
+ "LXcyVMvjR/CZ7dOSNgLGAkFfOoPhcF/OjSm4DrGPe3GiBxhwXTBjwJ5TIgEDkVIx\n"
+ "ZVo5r7gPCwKBgQCOvsZo/Q4hql2jXNqxGuj9PVkUBNFTI4agWEYyox7ECdlxjks5\n"
+ "vUOd5/1YvG+JXJgEcSbWRh8volDdL7qXnx0P881a6/aO35ybcKK58kvd62gEGEsf\n"
+ "1jUAOmmTAp2y7SVK7EOp8RY370b2oZxSR0XZrUXQJ3F22wV98ZVAfoLqZQKBgDIr\n"
+ "PdunbezAn5aPBOX/bZdZ6UmvbZYwVrHZxIKz2214U/STAu3uj2oiQX6ZwTzBDMjn\n"
+ "IKr+z74nnaCP+eAGhztabTPzXqXNUNUn/Zshl60BwKJToTYeJXJTY+eZRhpGB05w\n"
+ "Mz7M+Wgvvg2WZcllRnuV0j0UTysLhz1qle0vzLR9AoGBAOukkFFm2RLm9N1P3gI8\n"
+ "mUadeAlYRZ5o0MvumOHaB5pDOCKhrqAhop2gnM0f5uSlapCtlhj0Js7ZyS3Giezg\n"
+ "38oqAhAYxy2LMoLD7UtsHXNp0OnZ22djcDwh+Wp2YORm7h71yOM0NsYubGbp+CmT\n"
+ "Nw9bewRvqjySBlDJ9/aNSeEY\n"
+ "-----END PRIVATE KEY-----";

public static GitHubAppCredentials createCredentials(final String id) {
return new GitHubAppCredentials(CredentialsScope.GLOBAL, id, "sample", "54321", Secret.fromString(PRIVATE_KEY));
}

public static GitHubAppCredentials createCredentials(final String id, final String owner) {
final var credentials = createCredentials(id);
credentials.setOwner(owner);
return credentials;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.jenkinsci.plugins.github_branch_source;

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.domains.Domain;
import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import jenkins.branch.BranchSource;
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
import org.junit.Before;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;

@Issue("JENKINS-73474")
public class GitHubAppCredentialsContextualizationTest extends AbstractGitHubWireMockTest {

@Before
public void setUpWireMock() throws Exception {
GitHubConfiguration.get().setApiRateLimitChecker(ApiRateLimitChecker.ThrottleOnOver);

// Add wiremock responses for App, App Installation, and App Installation Token
githubApi.stubFor(get(urlEqualTo("/app"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json; charset=utf-8")
.withBodyFile("../AppCredentials/files/body-mapping-githubapp-app.json")));
githubApi.stubFor(get(urlEqualTo("/app/installations"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json; charset=utf-8")
.withBodyFile("../AppCredentials/files/body-mapping-githubapp-installations.json")));
githubApi.stubFor(post(urlEqualTo("/app/installations/654321/access_tokens"))
.withRequestBody(equalToJson(
"{\"permissions\":{\"pull_requests\":\"write\",\"metadata\":\"read\",\"checks\":\"write\",\"contents\":\"read\"}}",
true,
false))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json; charset=utf-8")
.withBody("{\"token\":\"super-secret-token\",\"expires_at\":\"" + createTokenExpiration()
+ "\"}")));

// Add wiremock responses for Repository
githubApi.stubFor(get(urlEqualTo("/repos/cloudbeers/multibranch-demo"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json; charset=utf-8")
.withBodyFile("../contextualization/body-repos-cloudbeers-multibranch-demo.json")));
githubApi.stubFor(get(urlEqualTo("/repos/cloudbeers/multibranch-demo/branches"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json; charset=utf-8")
.withBodyFile("../contextualization/body-repos-cloudbeers-multibranch-demo-branches.json")));
githubApi.stubFor(get(urlEqualTo("/repos/cloudbeers/multibranch-demo/contents/?ref=refs%2Fheads%2Fmaster"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json; charset=utf-8")
.withBodyFile("../contextualization/body-repos-cloudbeers-multibranch-demo-contents.json")));
githubApi.stubFor(get(urlEqualTo("/repos/cloudbeers/multibranch-demo/pulls?state=open"))
.willReturn(aResponse()
.withHeader("Content-Type", "application/json; charset=utf-8")
.withBodyFile("../contextualization/body-repos-cloudbeers-multibranch-demo-pulls.json")));
}

@Test
public void ownerMustBeInferredFromRepository() throws Exception {
final var store = CredentialsProvider.lookupStores(r.jenkins).iterator().next();

final var credentials = GitHubApp.createCredentials("myAppCredentialsWithoutOwner");
store.addCredentials(Domain.global(), credentials);
credentials.setApiUri(githubApi.baseUrl());

final var scmSource = new GitHubSCMSource("cloudbeers", "multibranch-demo", null, false);
scmSource.setTraits(Arrays.asList(new BranchDiscoveryTrait(true, true)));
scmSource.setCredentialsId(credentials.getId());
scmSource.setApiUri(githubApi.baseUrl());

final var multiBranchProject = r.jenkins.createProject(WorkflowMultiBranchProject.class, "multibranch-demo");
multiBranchProject.getSourcesList().add(new BranchSource(scmSource));
multiBranchProject.scheduleBuild2(0).getFuture().get();

final var branchProject = multiBranchProject.getItem("master");
assertThat(branchProject, notNullValue());
}

private String createTokenExpiration() {
// This token will go stale at the soonest allowed time but will not
// expire for the duration of the test
// Format: 2019-08-10T05:54:58Z
return DateTimeFormatter.ISO_INSTANT.format(
Instant.now().plus(Duration.ofMinutes(10)).truncatedTo(ChronoUnit.SECONDS));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.equalTo;
import static org.jenkinsci.plugins.github_branch_source.Connector.createGitHubBuilder;
import static org.junit.Assert.assertThrows;

import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.CredentialsScope;
import com.cloudbees.plugins.credentials.CredentialsStore;
import com.cloudbees.plugins.credentials.domains.Domain;
import com.github.tomakehurst.wiremock.http.RequestMethod;
Expand All @@ -19,7 +19,6 @@
import hudson.model.Result;
import hudson.model.Slave;
import hudson.model.StringParameterDefinition;
import hudson.util.Secret;
import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
Expand Down Expand Up @@ -56,40 +55,6 @@ public class GithubAppCredentialsTest extends AbstractGitHubWireMockTest {
private static GitHubAppCredentials appCredentials, appCredentialsNoOwner;
private static LogRecorder logRecorder;

// https://stackoverflow.com/a/22176759/4951015
public static final String PKCS8_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n"
+
// Windows line ending
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD7vHsVwyDV8cj7\r\n"
+
// This should also work
"5yR4WWl6rlgf/e5zmeBgtm0PCgnitcSbD5FU33301DPY5a7AtqVBOwEnE14L9XS7\r"
+ "ov61U+x1m4aQmqR/dPQaA2ayh2cYPszWNQMp42ArDIfg7DhSrvsRJKHsbPXlPjqe\n"
+ "c0udLqhSLVIO9frNLf+dAsLsgYk8O39PKGb33akGG7tWTe0J+akNQjgbS7vOi8sS\n"
+ "NLwHIdYfz/Am+6Xmm+J4yVs6+Xt3kOeLdFBkz8H/HGsJq854MbIAK/HuId1MOPS0\n"
+ "cDWh37tzRsM+q/HZzYRkc5bhNKw/Mj9jN9jD5GH0Lfea0QFedjppf1KvWdcXn+/W\n"
+ "M7OmyfhvAgMBAAECggEAN96H7reExRbJRWbySCeH6mthMZB46H0hODWklK7krMUs\n"
+ "okFdPtnvKXQjIaMwGqMuoACJa/O3bq4GP1KYdwPuOdfPkK5RjdwWBOP2We8FKXNe\n"
+ "oLfZQOWuxT8dtQSYJ3mgTRi1OzSfikY6Wko6YOMnBj36tUlQZVMtJNqlCjphi9Uz\n"
+ "6EyvRURlDG8sBBbC7ods5B0789qk3iGH/97ia+1QIqXAUaVFg3/BA6wkxkbNG2sN\n"
+ "tqULgVYTw32Oj/Y/H1Y250RoocTyfsUS3I3aPIlnvcgp2bugWqDyYJ58nDIt3Pku\n"
+ "fjImWrNz/pNiEs+efnb0QEk7m5hYwxmyXN4KRSv0OQKBgQD+I3Y3iNKSVr6wXjur\n"
+ "OPp45fxS2sEf5FyFYOn3u760sdJOH9fGlmf9sDozJ8Y8KCaQCN5tSe3OM+XDrmiw\n"
+ "Cu/oaqJ1+G4RG+6w1RJF+5Nfg6PkUs7eJehUgZ2Tox8Tg1mfVIV8KbMwNi5tXpug\n"
+ "MVmA2k9xjc4uMd2jSnSj9NAqrQKBgQD9lIO1tY6YKF0Eb0Qi/iLN4UqBdJfnALBR\n"
+ "MjxYxqqI8G4wZEoZEJJvT1Lm6Q3o577N95SihZoj69tb10vvbEz1pb3df7c1HEku\n"
+ "LXcyVMvjR/CZ7dOSNgLGAkFfOoPhcF/OjSm4DrGPe3GiBxhwXTBjwJ5TIgEDkVIx\n"
+ "ZVo5r7gPCwKBgQCOvsZo/Q4hql2jXNqxGuj9PVkUBNFTI4agWEYyox7ECdlxjks5\n"
+ "vUOd5/1YvG+JXJgEcSbWRh8volDdL7qXnx0P881a6/aO35ybcKK58kvd62gEGEsf\n"
+ "1jUAOmmTAp2y7SVK7EOp8RY370b2oZxSR0XZrUXQJ3F22wV98ZVAfoLqZQKBgDIr\n"
+ "PdunbezAn5aPBOX/bZdZ6UmvbZYwVrHZxIKz2214U/STAu3uj2oiQX6ZwTzBDMjn\n"
+ "IKr+z74nnaCP+eAGhztabTPzXqXNUNUn/Zshl60BwKJToTYeJXJTY+eZRhpGB05w\n"
+ "Mz7M+Wgvvg2WZcllRnuV0j0UTysLhz1qle0vzLR9AoGBAOukkFFm2RLm9N1P3gI8\n"
+ "mUadeAlYRZ5o0MvumOHaB5pDOCKhrqAhop2gnM0f5uSlapCtlhj0Js7ZyS3Giezg\n"
+ "38oqAhAYxy2LMoLD7UtsHXNp0OnZ22djcDwh+Wp2YORm7h71yOM0NsYubGbp+CmT\n"
+ "Nw9bewRvqjySBlDJ9/aNSeEY\n"
+ "-----END PRIVATE KEY-----";

@Rule
public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();

Expand All @@ -106,16 +71,9 @@ public static void setUpJenkins() throws Exception {
// Add credential (Must have valid private key for Jwt to work, but App doesn't have to actually
// exist)
store = CredentialsProvider.lookupStores(r.jenkins).iterator().next();
appCredentials = new GitHubAppCredentials(
CredentialsScope.GLOBAL, myAppCredentialsId, "sample", "54321", Secret.fromString(PKCS8_PRIVATE_KEY));
appCredentials.setOwner("cloudBeers");
appCredentials = GitHubApp.createCredentials(myAppCredentialsId, "cloudBeers");
store.addCredentials(Domain.global(), appCredentials);
appCredentialsNoOwner = new GitHubAppCredentials(
CredentialsScope.GLOBAL,
myAppCredentialsNoOwnerId,
"sample",
"54321",
Secret.fromString(PKCS8_PRIVATE_KEY));
appCredentialsNoOwner = GitHubApp.createCredentials(myAppCredentialsNoOwnerId);
store.addCredentials(Domain.global(), appCredentialsNoOwner);

// Add agent
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"name": "master",
"commit": {
"sha": "ba1fdaa6c2088cecea85333e763710cd3f39688f",
"url": "https://api.github.com/repos/cloudbeers/multibranch-demo/commits/ba1fdaa6c2088cecea85333e763710cd3f39688f"
},
"protected": false
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
[
{
"name": "Jenkinsfile",
"path": "Jenkinsfile",
"sha": "98c0f1dad6a7874ff6a7ccc4dd72a5519ec8fdba",
"size": 351,
"url": "https://api.github.com/repos/cloudbeers/multibranch-demo/contents/Jenkinsfile?ref=refs/heads/master",
"html_url": "https://github.com/cloudbeers/multibranch-demo/blob/refs/heads/master/Jenkinsfile",
"git_url": "https://api.github.com/repos/cloudbeers/multibranch-demo/git/blobs/98c0f1dad6a7874ff6a7ccc4dd72a5519ec8fdba",
"download_url": "https://raw.githubusercontent.com/cloudbeers/multibranch-demo/refs/heads/master/Jenkinsfile",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/cloudbeers/multibranch-demo/contents/Jenkinsfile?ref=refs/heads/master",
"git": "https://api.github.com/repos/cloudbeers/multibranch-demo/git/blobs/98c0f1dad6a7874ff6a7ccc4dd72a5519ec8fdba",
"html": "https://github.com/cloudbeers/multibranch-demo/blob/refs/heads/master/Jenkinsfile"
}
},
{
"name": "README.md",
"path": "README.md",
"sha": "59e16b0d408539ab4f23d64f2cafd33495eb21ad",
"size": 131,
"url": "https://api.github.com/repos/cloudbeers/multibranch-demo/contents/README.md?ref=refs/heads/master",
"html_url": "https://github.com/cloudbeers/multibranch-demo/blob/refs/heads/master/README.md",
"git_url": "https://api.github.com/repos/cloudbeers/multibranch-demo/git/blobs/59e16b0d408539ab4f23d64f2cafd33495eb21ad",
"download_url": "https://raw.githubusercontent.com/cloudbeers/multibranch-demo/refs/heads/master/README.md",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/cloudbeers/multibranch-demo/contents/README.md?ref=refs/heads/master",
"git": "https://api.github.com/repos/cloudbeers/multibranch-demo/git/blobs/59e16b0d408539ab4f23d64f2cafd33495eb21ad",
"html": "https://github.com/cloudbeers/multibranch-demo/blob/refs/heads/master/README.md"
}
},
{
"name": "hello-world.go",
"path": "hello-world.go",
"sha": "b64817ed596a1dae19dfa3f8a6e6bc26506cc645",
"size": 75,
"url": "https://api.github.com/repos/cloudbeers/multibranch-demo/contents/hello-world.go?ref=refs/heads/master",
"html_url": "https://github.com/cloudbeers/multibranch-demo/blob/refs/heads/master/hello-world.go",
"git_url": "https://api.github.com/repos/cloudbeers/multibranch-demo/git/blobs/b64817ed596a1dae19dfa3f8a6e6bc26506cc645",
"download_url": "https://raw.githubusercontent.com/cloudbeers/multibranch-demo/refs/heads/master/hello-world.go",
"type": "file",
"_links": {
"self": "https://api.github.com/repos/cloudbeers/multibranch-demo/contents/hello-world.go?ref=refs/heads/master",
"git": "https://api.github.com/repos/cloudbeers/multibranch-demo/git/blobs/b64817ed596a1dae19dfa3f8a6e6bc26506cc645",
"html": "https://github.com/cloudbeers/multibranch-demo/blob/refs/heads/master/hello-world.go"
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Loading

0 comments on commit 11ce4fe

Please sign in to comment.