/* * The MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.jenkinsci.plugins.credentialsbinding.impl; import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; import com.cloudbees.plugins.credentials.BaseCredentials; import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.domains.Domain; import edu.umd.cs.findbugs.annotations.NonNull; import hudson.FilePath; import hudson.Functions; import hudson.util.Secret; import org.jenkinsci.plugins.credentialsbinding.MultiBinding; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.jenkinsci.plugins.workflow.steps.StepConfigTester; import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runners.model.Statement; import org.jvnet.hudson.test.RestartableJenkinsRule; import java.io.Serializable; import java.util.*; import static org.junit.Assert.*; public class SSHUserPrivateKeyTest { @Rule public RestartableJenkinsRule story = new RestartableJenkinsRule(); @Rule public TemporaryFolder tmp = new TemporaryFolder(); private static class DummyPrivateKey extends BaseCredentials implements SSHUserPrivateKey, Serializable { private final String id; private final String user; private final Secret passphrase; private final String keyContent; DummyPrivateKey(String id, String user, String passphrase, final String keyContent) { this.id = id; this.user = user; this.passphrase = Secret.fromString(passphrase); this.keyContent = keyContent; } @NonNull @Override public String getId() { return id; } @NonNull @Override public String getPrivateKey() { return keyContent; } @Override public Secret getPassphrase() { return passphrase; } @NonNull @Override public List<String> getPrivateKeys() { return Arrays.asList(keyContent); } @NonNull @Override public String getUsername() { return user; } @NonNull @Override public String getDescription() { return ""; } @Override public CredentialsScope getScope() { return CredentialsScope.GLOBAL; } } @Test public void configRoundTrip() throws Exception { story.addStep(new Statement() { @Override public void evaluate() throws Throwable { SSHUserPrivateKey c = new DummyPrivateKey("creds", "bob", "secret", "the-key"); CredentialsProvider.lookupStores(story.j.jenkins).iterator().next().addCredentials(Domain.global(), c); SSHUserPrivateKeyBinding binding = new SSHUserPrivateKeyBinding("keyFile", "creds"); binding.setPassphraseVariable("passphrase"); binding.setUsernameVariable("user"); BindingStep s = new StepConfigTester(story.j).configRoundTrip(new BindingStep( Collections.<MultiBinding>singletonList(binding))); story.j.assertEqualDataBoundBeans(s.getBindings(), Collections.singletonList(binding)); } }); } @Test public void basics() throws Exception { final String credentialsId = "creds"; final String username = "bob"; final String passphrase = "s3cr3t"; final String keyContent = "the-key"; story.addStep(new Statement() { @Override public void evaluate() throws Throwable { SSHUserPrivateKey c = new DummyPrivateKey(credentialsId, username, passphrase, keyContent); CredentialsProvider.lookupStores(story.j.jenkins).iterator().next().addCredentials(Domain.global(), c); WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "p"); String script; if (Functions.isWindows()) { script = " bat '''\n" + " @echo off\n" + " echo %THEUSER%:%THEPASS% > out.txt\n" + " type \"%THEKEY%\" > key.txt" + " '''\n"; } else { script = " sh '''\n" + " echo $THEUSER:$THEPASS > out.txt\n" + " cat \"$THEKEY\" > key.txt" + " '''\n"; } p.setDefinition(new CpsFlowDefinition("" + "node {\n" + " withCredentials([sshUserPrivateKey(keyFileVariable: 'THEKEY', passphraseVariable: 'THEPASS', usernameVariable: 'THEUSER', credentialsId: '" + credentialsId + "')]) {\n" + " semaphore 'basics'\n" + script + " }\n" + "}", true)); WorkflowRun b = p.scheduleBuild2(0).waitForStart(); SemaphoreStep.waitForStart("basics/1", b); } }); story.addStep(new Statement() { @Override public void evaluate() throws Throwable { WorkflowJob p = story.j.jenkins.getItemByFullName("p", WorkflowJob.class); assertNotNull(p); WorkflowRun b = p.getBuildByNumber(1); assertNotNull(b); SemaphoreStep.success("basics/1", null); story.j.waitForCompletion(b); story.j.assertBuildStatusSuccess(b); story.j.assertLogNotContains(passphrase, b); FilePath out = story.j.jenkins.getWorkspaceFor(p).child("out.txt"); assertTrue(out.exists()); assertEquals(username + ":" + passphrase, out.readToString().trim()); FilePath key = story.j.jenkins.getWorkspaceFor(p).child("key.txt"); assertTrue(key.exists()); assertEquals(keyContent, key.readToString().trim()); } }); } @Test public void noUsernameOrPassphrase() throws Exception { final String credentialsId = "creds"; final String keyContent = "the-key"; story.addStep(new Statement() { @Override public void evaluate() throws Throwable { SSHUserPrivateKey c = new DummyPrivateKey(credentialsId, "", "", keyContent); CredentialsProvider.lookupStores(story.j.jenkins).iterator().next().addCredentials(Domain.global(), c); WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "p"); String script; if (Functions.isWindows()) { script = " bat '''\n" + " @echo off\n" + " type \"%THEKEY%\" > key.txt" + " '''\n"; } else { script = " sh '''\n" + " cat \"$THEKEY\" > key.txt" + " '''\n"; } p.setDefinition(new CpsFlowDefinition("" + "node {\n" + " withCredentials([sshUserPrivateKey(keyFileVariable: 'THEKEY', credentialsId: '" + credentialsId + "')]) {\n" + " semaphore 'basics'\n" + script + " }\n" + "}", true)); WorkflowRun b = p.scheduleBuild2(0).waitForStart(); SemaphoreStep.waitForStart("basics/1", b); } }); story.addStep(new Statement() { @Override public void evaluate() throws Throwable { WorkflowJob p = story.j.jenkins.getItemByFullName("p", WorkflowJob.class); assertNotNull(p); WorkflowRun b = p.getBuildByNumber(1); assertNotNull(b); SemaphoreStep.success("basics/1", null); story.j.waitForCompletion(b); story.j.assertBuildStatusSuccess(b); FilePath key = story.j.jenkins.getWorkspaceFor(p).child("key.txt"); assertTrue(key.exists()); assertEquals(keyContent, key.readToString().trim()); } }); } }