Skip to content

Commit

Permalink
[JENKINS-73471] Test GitSCM uses the correct credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
Dohbedoh committed Sep 23, 2024
1 parent 413c117 commit fb52504
Showing 1 changed file with 94 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.cloudbees.jenkins.plugins.bitbucket;

import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketApi;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketHref;
import com.cloudbees.jenkins.plugins.bitbucket.api.BitbucketRepository;
import com.cloudbees.jenkins.plugins.bitbucket.client.BitbucketCloudApiClient;
import com.cloudbees.jenkins.plugins.bitbucket.endpoints.BitbucketCloudEndpoint;
import com.cloudbees.jenkins.plugins.bitbucket.server.client.BitbucketServerAPIClient;
import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey;
Expand All @@ -14,22 +14,29 @@
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.UserRemoteConfig;
import hudson.plugins.git.extensions.impl.BuildChooserSetting;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import jenkins.plugins.git.AbstractGitSCMSource;
import jenkins.plugins.git.GitSCMSourceDefaults;
import jenkins.plugins.git.GitSampleRepoRule;
import org.jenkinsci.plugins.workflow.cps.CpsScmFlowDefinition;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.jenkinsci.plugins.workflow.job.WorkflowRun;
import org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.recipes.WithTimeout;
import org.mockito.junit.MockitoJUnitRunner;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
Expand All @@ -44,25 +51,64 @@ public class BitbucketSCMSourceBuildTest {

private static final String CLOUD_REPO_OWNER = "cloudbeers";
private static final String REPO_NAME = "stunning-adventure";
private static final String BRANCH_NAME = "branch1";
private static final String COMMIT_HASH = "e851558f77c098d21af6bb8cc54a423f7cf12147";
private static final String BRANCH_NAME = "master";
// Test private key from ssh-credentials test case
// https://github.com/jenkinsci/ssh-credentials-plugin/blob/343.v884f71d78167/src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyTest/readOldCredentials/credentials.xml
private static final String PRIVATE_KEY = "-----BEGIN RSA PRIVATE KEY-----\n" +
"MIIEpAIBAAKCAQEAu1r+HHzmpybc4iwoP5+44FjvcaMkNEWeGQZlmPwLx70XW8+8\n" +
"3d2BMLjhcw1zLsYG3FWpCyn8cB1OmjKiGjvnP5EBoAolvh3qOcWKyWlVGWGgs10B\n" +
"0Cgnd3OBXRPQd1cBdiQZmmeCrrH0OjQefYIF2WYN+F8iuNGraAaRsXLgITanjTb1\n" +
"6w1dnk+KLpU2J5G6kG1f/Qxl4pgny80S/3TktqoknbOrYDMOSA1Zdww39cpXJHp6\n" +
"feEs8tavC93rOsR2O4ZfVUCjTFAF5FtIdRv3LXY3Q5W/AyY1h45Wk6mMVnEluFjB\n" +
"aA+gzVAVaHFQfuhwoj4B7jWCmfHsPG1WmyK0YQIDAQABAoIBADt1qlXiMdV0mP9S\n" +
"okdm6maQ8xTugKvyODWa+R1vSFHQqhwiNr927+xFkI9SAm8iu8SrjuWTIqF2O57m\n" +
"WNnYjxB2dbyT29yVY+OH1P8M5cwTVsv1xYCJbdUUHEcs5akqPLWAyXteRHQq1+as\n" +
"6cxNOov/PonHr55WNH7kLtLRMV54jZ68nrEh5pWdabFa0f7d/ByIvYRJm7lpjtSp\n" +
"EBp5AseXzSg2EZP3HDPYYPDHK0tMPginz9+YuQCQFoMYAkVZoKFJGsWICktd5Uk7\n" +
"wveOJLOfMng1Iww6CEc871GcLn5LbafLWRxjZssK2Z1fC+pZYLLZPeKDMSxoRXm6\n" +
"PShUC1ECgYEA623dmwJNYfVRgAgOYhhcxzH4TAXUmUIpadEUjOkAhe3w/abDawFT\n" +
"9ianhqfhTjSZGBtUttcN40Vy+P4bsqfQKZ7p6KzrdR2zWjlYcICWhZDZPGmMxpMZ\n" +
"mUFhZXsLRVhn8qed8w1t6eju7S+t9satKIMC/KrhNsFzjrgbU9eC+m0CgYEAy7nN\n" +
"gMwQeGxAQSJr9H7eKkthnjMe77rLIAZEbDJhcwYVz+Iie/E4hjESQ+IuvXa1VawD\n" +
"O6cD0wWdOhH2McNdMNIbM4IOaO/TOaR5jfQwBWmb4iG2BZQWQQE/HUBnoJQWUhqm\n" +
"b+D+s4bHh4J+bs+ptgg9Sd9V+VxJBcu2QDmI6UUCgYBTb1pMJyK5hrFdiH1gcnXe\n" +
"+myetKpFrlby83AvCBxxWoQ/wKwc7hmNcOGKLVEB4E4pZvY83jZDx0cZyySRyjtR\n" +
"pMoM9ct0dBQt84jORiQSLeVvLZEAhv1ZfPxBdLvn1Y7xRkoJ60Z60Vxrnqwueva/\n" +
"Fr8mQIEUYLbNa53ztrrqeQKBgQCqOY4k2F3KwWjPA9wAZyFrZaEjdsOavBGNqK7z\n" +
"WQVj/umq0eDOfzgjqE0Cu7MiTFYoR5pL9bmUUVSWePuliQANEwH3f+xackmkGHIY\n" +
"0rhtTVkbEd/tuVb+6fO6lV4BJrufzvTS9sTbbPq7l6XdIVdE6o2LdDl6Kko5tYWL\n" +
"FIf5oQKBgQDLHK/9NTb3VHp+Qriu2Vp8Pnaw6YF6pETfgjyrH2ULSW/R07v+AECC\n" +
"sPr+d/hx2MQWp54HglY8lv98rOrRjMiRw1GVoXs+Ut9vkupmrpvzNE7ITl0tzBqD\n" +
"sroT/IHW2jKMD0v8kKLUnKCZYzlw0By7+RvJ8lgzHB0D71f6EC1UWg==\n" +
"-----END RSA PRIVATE KEY-----\n";

@Rule
public JenkinsRule j = new JenkinsRule();

@Rule
public GitSampleRepoRule sampleRepo = new GitSampleRepoRule();

@ClassRule
public static JenkinsRule j = new JenkinsRule();

@Test
@Issue("JENKINS-73471")
public void buildWhenSetSSHCheckoutTraitThenNoAuthenticatorExtension() throws IOException, InterruptedException {
@WithTimeout(30)
public void buildWhenSetSSHCheckoutTraitThenNoAuthenticatorExtension() throws Exception {
String jenkinsFile = "Jenkinsfile";
sampleRepo.init();
sampleRepo.write(jenkinsFile, "node { checkout scm }");
sampleRepo.git("add", jenkinsFile);
sampleRepo.git("commit", "--all", "--message=defined");

StandardUsernameCredentials userPassCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL,
"user-pass", null, "user", "pass");
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
.addCredentials(Domain.global(), userPassCredentials);
StandardUsernameCredentials sshCredentials = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "user-key", "user",
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(""), null, null);
new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(PRIVATE_KEY), null, null);
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
.addCredentials(Domain.global(), sshCredentials);

WorkflowMultiBranchProject owner = j.createProject(WorkflowMultiBranchProject.class);
WorkflowMultiBranchProject owner = j.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
BitbucketSCMSource instance = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
instance.setOwner(owner);
instance.setCredentialsId(userPassCredentials.getId());
Expand All @@ -72,63 +118,90 @@ public void buildWhenSetSSHCheckoutTraitThenNoAuthenticatorExtension() throws IO

BitbucketRepository repository = mock(BitbucketRepository.class);
when(repository.getLinks()).thenReturn(Map.of("clone", List.of(
new BitbucketHref("http", String.format("https://bitbucket.org/%s/%s.git", CLOUD_REPO_OWNER, REPO_NAME)),
new BitbucketHref("ssh", String.format("ssh://[email protected]/%s/%s.git", CLOUD_REPO_OWNER, REPO_NAME))
new BitbucketHref("http", sampleRepo.toString()),
new BitbucketHref("ssh", String.format("ssh://user@localhost/%s", sampleRepo))
)));
BitbucketCloudApiClient client = mock(BitbucketCloudApiClient.class);
BitbucketApi client = mock(BitbucketApi.class);
BitbucketMockApiFactory.add(BitbucketCloudEndpoint.SERVER_URL, client);
when(client.getRepository()).thenReturn(repository);

BranchSCMHead head = new BranchSCMHead(BRANCH_NAME);
AbstractGitSCMSource.SCMRevisionImpl revision =
new AbstractGitSCMSource.SCMRevisionImpl(head, COMMIT_HASH);
new AbstractGitSCMSource.SCMRevisionImpl(head, sampleRepo.head());
GitSCM build = (GitSCM)instance.build(head, revision);
assertThat(build.getUserRemoteConfigs().size(), is(1));
UserRemoteConfig remoteConfig = build.getUserRemoteConfigs().get(0);
assertThat(remoteConfig.getUrl(), is(String.format("ssh://[email protected]/%s/%s.git", CLOUD_REPO_OWNER, REPO_NAME)));
assertThat(remoteConfig.getUrl(), is(String.format("ssh://user@localhost/%s", sampleRepo)));
assertThat(remoteConfig.getRefspec(), is(String.format("+refs/heads/%s:refs/remotes/origin/%s", BRANCH_NAME, BRANCH_NAME)));
assertThat(remoteConfig.getCredentialsId(), is(sshCredentials.getId()));
assertThat(build.getExtensions(), containsInAnyOrder(
instanceOf(BuildChooserSetting.class),
instanceOf(GitSCMSourceDefaults.class))
);

// Create a Pipeline with CpsScmFlowDefinition based of the GitSCM produced
// Then check that the checkout uses GIT_SSH from the git-client logs
WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "testGitScm");
job.setDefinition(new CpsScmFlowDefinition(build, jenkinsFile));
WorkflowRun run = job.scheduleBuild2(0).get();

ByteArrayOutputStream byteArrayOutStr = new ByteArrayOutputStream();
run.writeWholeLogTo(byteArrayOutStr);
assertThat(byteArrayOutStr.toString(StandardCharsets.UTF_8), containsString("using GIT_SSH to set credentials"));
}

@Test
public void buildBasicAuthThenAuthenticatorExtension() throws IOException, InterruptedException {
@WithTimeout(30)
public void buildBasicAuthThenAuthenticatorExtension() throws Exception {
String jenkinsFile = "Jenkinsfile";
sampleRepo.init();
sampleRepo.write(jenkinsFile, "node { checkout scm }");
sampleRepo.git("add", jenkinsFile);
sampleRepo.git("commit", "--all", "--message=defined");

StandardUsernameCredentials userPassCredentials = new UsernamePasswordCredentialsImpl(CredentialsScope.GLOBAL,
"user-pass", null, "user", "pass");
CredentialsProvider.lookupStores(j.jenkins).iterator().next()
.addCredentials(Domain.global(), userPassCredentials);

WorkflowMultiBranchProject owner = j.createProject(WorkflowMultiBranchProject.class);
WorkflowMultiBranchProject owner = j.createProject(WorkflowMultiBranchProject.class, "testMultibranch");
BitbucketSCMSource instance = new BitbucketSCMSource(CLOUD_REPO_OWNER, REPO_NAME);
instance.setOwner(owner);
instance.setCredentialsId(userPassCredentials.getId());
instance.setTraits(List.of(new BranchDiscoveryTrait(1)));

BitbucketRepository repository = mock(BitbucketRepository.class);
when(repository.getLinks()).thenReturn(Map.of("clone", List.of(
new BitbucketHref("http", String.format("https://bitbucket.org/%s/%s.git", CLOUD_REPO_OWNER, REPO_NAME)),
new BitbucketHref("ssh", String.format("ssh://[email protected]/%s/%s.git", CLOUD_REPO_OWNER, REPO_NAME))
new BitbucketHref("http", sampleRepo.toString()),
new BitbucketHref("ssh", String.format("ssh://localhost:%s", sampleRepo))
)));
BitbucketServerAPIClient client = mock(BitbucketServerAPIClient.class);
BitbucketMockApiFactory.add(BitbucketCloudEndpoint.SERVER_URL, client);
when(client.getRepository()).thenReturn(repository);

BranchSCMHead head = new BranchSCMHead(BRANCH_NAME);
AbstractGitSCMSource.SCMRevisionImpl revision =
new AbstractGitSCMSource.SCMRevisionImpl(head, COMMIT_HASH);
new AbstractGitSCMSource.SCMRevisionImpl(head, sampleRepo.head());
GitSCM build = (GitSCM)instance.build(head, revision);
assertThat(build.getUserRemoteConfigs().size(), is(1));
UserRemoteConfig remoteConfig = build.getUserRemoteConfigs().get(0);
assertThat(remoteConfig.getUrl(), is(String.format("https://bitbucket.org/%s/%s.git", CLOUD_REPO_OWNER, REPO_NAME)));
assertThat(remoteConfig.getUrl(), is(sampleRepo.toString()));
assertThat(remoteConfig.getRefspec(), is(String.format("+refs/heads/%s:refs/remotes/origin/%s", BRANCH_NAME, BRANCH_NAME)));
assertThat(remoteConfig.getCredentialsId(), is(userPassCredentials.getId()));
assertThat(build.getExtensions(), containsInAnyOrder(
instanceOf(BuildChooserSetting.class),
instanceOf(GitSCMSourceDefaults.class),
instanceOf(GitClientAuthenticatorExtension.class)
));

// Create a Pipeline with CpsScmFlowDefinition based of the GitSCM produced
// Then check that the checkout scm uses GIT_ASKPASS from the git-client logs
WorkflowJob job = j.jenkins.createProject(WorkflowJob.class, "testGitScm");
job.setDefinition(new CpsScmFlowDefinition(build, jenkinsFile));
WorkflowRun run = job.scheduleBuild2(0).get();

ByteArrayOutputStream byteArrayOutStr = new ByteArrayOutputStream();
run.writeWholeLogTo(byteArrayOutStr);
assertThat(byteArrayOutStr.toString(StandardCharsets.UTF_8), containsString("using GIT_ASKPASS to set credentials"));
}
}

0 comments on commit fb52504

Please sign in to comment.