Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>5.9</version>
<version>5.17</version>
<relativePath />
</parent>

Expand Down Expand Up @@ -73,7 +73,7 @@
<changelist>999999-SNAPSHOT</changelist>
<!-- https://www.jenkins.io/doc/developer/plugin-development/choosing-jenkins-baseline/ -->
<jenkins.baseline>2.479</jenkins.baseline>
<jenkins.version>${jenkins.baseline}.1</jenkins.version>
<jenkins.version>${jenkins.baseline}.3</jenkins.version>
<gitHubRepo>jenkinsci/${project.artifactId}-plugin</gitHubRepo>
</properties>

Expand All @@ -95,7 +95,7 @@
<dependency>
<groupId>io.jenkins.tools.bom</groupId>
<artifactId>bom-${jenkins.baseline}.x</artifactId>
<version>3893.v213a_42768d35</version>
<version>4948.vcf1d17350668</version>
<scope>import</scope>
<type>pom</type>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void nonCompliantKeysLaunchExceptionTest() throws Throwable {
rule.then(BasicSSHUserPrivateKeyFIPSTest::checkNonCompliantKeysLaunchException);
}

private static void checkNonCompliantKeysLaunchException(JenkinsRule r) throws IOException{
private static void checkNonCompliantKeysLaunchException(JenkinsRule r) throws Exception {
new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "no-key", "user",
null, null, "no key provided doesn't throw exceptions");
assertThrows(IllegalArgumentException.class, () -> new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "nopass-openssh-ed25519", "user",
Expand Down Expand Up @@ -66,7 +66,7 @@ public void invalidKeyIsNotSavedInFIPSModeTest() throws Throwable {
rule.then(BasicSSHUserPrivateKeyFIPSTest::checkInvalidKeyIsNotSavedInFIPSMode);
}

private static void checkInvalidKeyIsNotSavedInFIPSMode(JenkinsRule r) throws IOException {
private static void checkInvalidKeyIsNotSavedInFIPSMode(JenkinsRule r) throws Exception {
BasicSSHUserPrivateKey entry = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, "rsa2048", "user", getKey("rsa2048"), "fipsvalidpassword", "RSA 1024 accepted key");
Iterator<CredentialsStore> stores = CredentialsProvider.lookupStores(r.jenkins).iterator();
assertTrue(stores.hasNext());
Expand Down Expand Up @@ -111,15 +111,15 @@ public void formValidationTest() throws Throwable {
rule.then(BasicSSHUserPrivateKeyFIPSTest::checkFormValidation);
}

private static void checkFormValidation(JenkinsRule r) throws IOException {
private static void checkFormValidation(JenkinsRule r) throws Exception {
BasicSSHUserPrivateKey.DirectEntryPrivateKeySource.DescriptorImpl descriptor = ExtensionList.lookupSingleton(BasicSSHUserPrivateKey.DirectEntryPrivateKeySource.DescriptorImpl.class);
FormValidation result = descriptor.doCheckPrivateKey(getKey("rsa2048").getPrivateKey().getPlainText(), "fipsvalidpassword");
assertNull(result.getMessage());
result = descriptor.doCheckPrivateKey(getKey("rsa1024").getPrivateKey().getPlainText(), "fipsvalidpassword");
assertFalse(result.getMessage().isBlank());
}

private static BasicSSHUserPrivateKey.DirectEntryPrivateKeySource getKey(String file) throws IOException {
private static BasicSSHUserPrivateKey.DirectEntryPrivateKeySource getKey(String file) throws Exception {
String keyText = FileUtils.readFileToString(Paths.get("src/test/resources/com/cloudbees/jenkins/plugins/sshcredentials/impl/BasicSSHUserPrivateKeyFIPSTest/nonCompliantKeysLaunchExceptionTest").resolve(file).toFile(), Charset.defaultCharset());
return new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(keyText);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,46 +38,53 @@
import hudson.security.ACL;
import jenkins.model.Jenkins;

import org.junit.Test;

import static hudson.cli.CLICommandInvoker.Matcher.failedWith;
import static hudson.cli.CLICommandInvoker.Matcher.succeeded;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.*;
import org.junit.Rule;
import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;
import org.jvnet.hudson.test.recipes.LocalData;

public class BasicSSHUserPrivateKeyTest {
@WithJenkins
class BasicSSHUserPrivateKeyTest {

final static String TESTKEY_ID = "bc07f814-78bd-4b29-93d4-d25b93285f93";
final static String TESTKEY_BEGIN = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAu1r+HHzmpybc4iwoP5+44FjvcaMkNEWeGQZlmPwLx70XW8+8";
final static String TESTKEY_END = "sroT/IHW2jKMD0v8kKLUnKCZYzlw0By7+RvJ8lgzHB0D71f6EC1UWg==\n-----END RSA PRIVATE KEY-----\n";
private static final String TESTKEY_ID = "bc07f814-78bd-4b29-93d4-d25b93285f93";
private static final String TESTKEY_BEGIN = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAu1r+HHzmpybc4iwoP5+44FjvcaMkNEWeGQZlmPwLx70XW8+8";
private static final String TESTKEY_END = "sroT/IHW2jKMD0v8kKLUnKCZYzlw0By7+RvJ8lgzHB0D71f6EC1UWg==\n-----END RSA PRIVATE KEY-----\n";

@Rule public JenkinsRule r = new JenkinsRule();
private JenkinsRule r;

@BeforeEach
void setUp(JenkinsRule rule) {
r = rule;
}

@LocalData
@Test
public void readOldCredentials() {
void readOldCredentials() {
SSHUserPrivateKey supk = CredentialsMatchers.firstOrNull(
CredentialsProvider.lookupCredentials(SSHUserPrivateKey.class, Hudson.get(), ACL.SYSTEM,
(List<DomainRequirement>)null),
CredentialsMatchers.withId(TESTKEY_ID));
assertNotNull(supk);
List<String> keyList = supk.getPrivateKeys();
assertNotNull(keyList);
assertEquals(keyList.size(), 1);
assertEquals(1, keyList.size());
String privateKey = keyList.get(0);
assertNotNull(privateKey);
assertTrue(privateKey.startsWith(TESTKEY_BEGIN));
assertTrue(privateKey.endsWith(TESTKEY_END));
}

@Test
public void ensureDirectEntryHasTrailingNewline() {
void ensureDirectEntryHasTrailingNewline() {
String key = (new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource("test")).getPrivateKey().getPlainText();
assertEquals("test\n", key);
}
Expand All @@ -87,20 +94,20 @@ public void ensureDirectEntryHasTrailingNewline() {
@Test
@LocalData
@Issue("SECURITY-440")
public void userWithoutRunScripts_cannotMigrateDangerousPrivateKeySource() throws Exception {
void userWithoutRunScripts_cannotMigrateDangerousPrivateKeySource() throws Exception {
Folder folder = r.jenkins.createProject(Folder.class, "folder1");

FilePath updateFolder = r.jenkins.getRootPath().child("update_folder.xml");

{ // as user with just configure, you cannot migrate
CLICommandInvoker.Result result = new CLICommandInvoker(r, new UpdateJobCommand())
.authorizedTo(Jenkins.READ, Job.READ, Job.CONFIGURE)
.withStdin(updateFolder.read())
.invokeWithArgs("folder1");

assertThat(result.stderr(), containsString("user is missing the Overall/Administer permission"));
assertThat(result, failedWith(1));

// config file not touched
String configFileContent = folder.getConfigFile().asString();
assertThat(configFileContent, not(containsString("FileOnMasterPrivateKeySource")));
Expand All @@ -111,7 +118,7 @@ public void userWithoutRunScripts_cannotMigrateDangerousPrivateKeySource() throw
.authorizedTo(Jenkins.ADMINISTER)
.withStdin(updateFolder.read())
.invokeWithArgs("folder1");

assertThat(result, succeeded());
String configFileContent = folder.getConfigFile().asString();
assertThat(configFileContent, containsString("BasicSSHUserPrivateKey"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@
import hudson.slaves.DumbSlave;
import jenkins.security.MasterToSlaveCallable;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.junit.jupiter.WithJenkins;

import java.io.Serial;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.Collections;
Expand All @@ -50,24 +52,31 @@
import java.util.logging.Logger;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertNotNull;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull;

public class TrileadSSHPasswordAuthenticatorTest {
@WithJenkins
class TrileadSSHPasswordAuthenticatorTest {

private Connection connection;
private StandardUsernamePasswordCredentials user;
private Object sshd;

@Rule public JenkinsRule r = new JenkinsRule();

@After
public void tearDown() {
private JenkinsRule r;

@BeforeEach
void setUp(JenkinsRule rule) {
r = rule;
user = (StandardUsernamePasswordCredentials) Items.XSTREAM.fromXML(Items.XSTREAM.toXML(new BasicSSHUserPassword(CredentialsScope.SYSTEM, null, "foobar", "foomanchu", null)));
}

@AfterEach
void tearDown() {
if (connection != null) {
connection.close();
connection = null;
}
if (sshd!=null) {
if (sshd != null) {
try {
invoke(sshd, "stop", new Class<?>[] {Boolean.TYPE}, new Object[] {true});
} catch (Throwable t) {
Expand All @@ -76,9 +85,10 @@ public void tearDown() {
}
}

// disabled as Apache MINA sshd does not provide easy mech for giving a Keyboard Interactive authenticator
// so this test relies on having a local sshd which is keyboard interactive only
public void dontTestKeyboardInteractive() throws Exception {
@Test
@Disabled("Apache MINA sshd does not provide easy mech for giving a Keyboard Interactive authenticator " +
"so this test relies on having a local sshd which is keyboard interactive only")
void dontTestKeyboardInteractive() throws Exception {
connection = new Connection("localhost");
connection.connect(new NoVerifier());
TrileadSSHPasswordAuthenticator instance =
Expand All @@ -91,13 +101,8 @@ public void dontTestKeyboardInteractive() throws Exception {
assertThat(instance.isAuthenticated(), is(true));
}

@Before
public void setUp() {
user =(StandardUsernamePasswordCredentials) Items.XSTREAM.fromXML(Items.XSTREAM.toXML(new BasicSSHUserPassword(CredentialsScope.SYSTEM, null, "foobar", "foomanchu", null)));
}

@Test
public void testPassword() throws Exception {
void testPassword() throws Exception {
sshd = createPasswordAuthenticatedSshServer();
invoke(sshd, "start", null, null);
int port = (Integer)invoke(sshd, "getPort", null, null);
Expand All @@ -111,11 +116,11 @@ public void testPassword() throws Exception {
assertThat(instance.isAuthenticated(), is(true));
}

private Object createPasswordAuthenticatedSshServer() throws InvocationTargetException, NoSuchMethodException, ClassNotFoundException, InstantiationException, IllegalAccessException {
private Object createPasswordAuthenticatedSshServer() throws Exception {
return createPasswordAuthenticatedSshServer(null);
}

private Object createPasswordAuthenticatedSshServer(final String username) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, InstantiationException {
private Object createPasswordAuthenticatedSshServer(final String username) throws Exception {
Object sshd = newDefaultSshServer();
Class<?> keyPairProviderClass = newKeyPairProviderClass();
Object provider = newProvider();
Expand All @@ -132,7 +137,7 @@ private Object createPasswordAuthenticatedSshServer(final String username) throw
}

@Test
public void testFactory() throws Exception {
void testFactory() throws Exception {
sshd = createPasswordAuthenticatedSshServer();
invoke(sshd, "start", null, null);
int port = (Integer)invoke(sshd, "getPort", null, null);
Expand All @@ -146,7 +151,7 @@ public void testFactory() throws Exception {
}

@Test
public void testFactoryAltUsername() throws Exception {
void testFactoryAltUsername() throws Exception {
sshd = createPasswordAuthenticatedSshServer("bill");
invoke(sshd, "start", null, null);
int port = (Integer)invoke(sshd, "getPort", null, null);
Expand All @@ -170,7 +175,7 @@ public void testFactoryAltUsername() throws Exception {
* Brings the {@link SSHAuthenticatorFactory} to a slave.
*/
@Test
public void testSlave() throws Exception {
void testSlave() throws Exception {
Object sshd = createPasswordAuthenticatedSshServer();
invoke(sshd, "start", null, null);

Expand Down Expand Up @@ -216,15 +221,16 @@ public Void call() throws Exception {
return null;
}

@Serial
private static final long serialVersionUID = 1L;
}

private Object invoke(Object target, String methodName, Class<?>[] parameterTypes, Object[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
private Object invoke(Object target, String methodName, Class<?>[] parameterTypes, Object[] args) throws Exception {
return target.getClass().getMethod(methodName, parameterTypes).invoke(target, args);
}

private Object newDefaultSshServer() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object server = null;
private Object newDefaultSshServer() throws Exception {
Object server;
Class<?> serverClass;
try {
serverClass = Class.forName("org.apache.sshd.SshServer");
Expand All @@ -238,7 +244,7 @@ private Object newDefaultSshServer() throws ClassNotFoundException, NoSuchMethod
return server;
}

private Class<?> newKeyPairProviderClass() throws ClassNotFoundException {
private Class<?> newKeyPairProviderClass() throws Exception {
Class<?> keyPairProviderClass;
try {
keyPairProviderClass = Class.forName("org.apache.sshd.common.KeyPairProvider");
Expand All @@ -249,15 +255,15 @@ private Class<?> newKeyPairProviderClass() throws ClassNotFoundException {
return keyPairProviderClass;
}

private Object newProvider() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
private Object newProvider() throws Exception {
Class<?> providerClass = Class.forName("org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider");
Object provider = providerClass.getConstructor().newInstance();
assertNotNull(provider);

return provider;
}

private Class<?> newAuthenticatorClass() throws ClassNotFoundException {
private Class<?> newAuthenticatorClass() throws Exception {
Class<?> authenticatorClass;
try {
authenticatorClass = Class.forName("org.apache.sshd.server.auth.password.PasswordAuthenticator");
Expand All @@ -268,7 +274,7 @@ private Class<?> newAuthenticatorClass() throws ClassNotFoundException {
return authenticatorClass;
}

private Object newAuthenticator(Class<?> authenticatorClass, final String userName) throws IllegalArgumentException {
private Object newAuthenticator(Class<?> authenticatorClass, final String userName) {
Object authenticator = Proxy.newProxyInstance(
authenticatorClass.getClassLoader(), new Class<?>[]{authenticatorClass}, (proxy, method, args) ->
method.getName().equals("authenticate") ?
Expand All @@ -278,8 +284,8 @@ private Object newAuthenticator(Class<?> authenticatorClass, final String userNa
return authenticator;
}

private Object newFactory() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Object factory = null;
private Object newFactory() throws Exception {
Object factory;
Class<?> factoryClass;
try {
factoryClass = Class.forName("org.apache.sshd.server.auth.UserAuthPassword$Factory");
Expand Down
Loading
Loading