Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Fixed #JENKINS-44586. add support for managing credentials #264

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
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
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
---
sudo: required
# Various issues have been logged aginst Travis-ci about Java 7 Support. Looks like it's broken on the latest Linux image.
# For now we use the old version of the image. In the longer term, we should consider moving to Java 8
group: deprecated-2017Q2

services:
- docker

Expand Down
1 change: 1 addition & 0 deletions jenkins-client-it-docker/plugins.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ job-dsl:1.41
config-file-provider:2.10.0
testng-plugin:1.10
cloudbees-folder: 5.12
xcode-plugin: 2.0.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.offbytwo.jenkins.integration;

import com.offbytwo.jenkins.JenkinsServer;
import com.offbytwo.jenkins.model.Plugin;
import com.offbytwo.jenkins.model.credentials.*;
import org.apache.commons.lang.RandomStringUtils;
import org.testng.SkipException;
import org.testng.annotations.Test;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import static org.testng.Assert.*;

@Test(groups = { Groups.NO_EXECUTOR_GROUP} )
public class NoExecutorStartedManageCredentialsIT extends AbstractJenkinsIntegrationCase {

@Test
public void credentialCRUDL() throws IOException {
List<Plugin> plugins = jenkinsServer.getPluginManager().getPlugins();
Plugin credentialPlugin = jenkinsServer.findPluginWithName("credentials");
if (credentialPlugin == null) {
throw new SkipException("No credentials plugin found. Skip Test");
}

String pluginVersion = credentialPlugin.getVersion();
if (pluginVersion.startsWith("1.")) {
runTest(jenkinsServer);

//test CertificateCredential with upload cert file. The 2.x version may throw exceptions.
CertificateCredential certificateCredential = new CertificateCredential();
certificateCredential.setId("certficateTest-" + RandomStringUtils.randomAlphanumeric(24));
certificateCredential.setCertificateSourceType(CertificateCredential.CERTIFICATE_SOURCE_TYPES.UPLOAD_CERT_FILE);
certificateCredential.setCertificateContent("testcert".getBytes());
certificateCredential.setPassword("testpasssword");

credentialOperations(jenkinsServer, certificateCredential);

} else {
runTest(jenkinsServer);

//test SecretTextCredential, this is v2 only
SecretTextCredential secretText = new SecretTextCredential();
secretText.setId("secrettextcredentialTest-" + RandomStringUtils.randomAlphanumeric(24));
secretText.setSecret("testsecrettext");

credentialOperations(jenkinsServer, secretText);
}
}

private void runTest(JenkinsServer jenkinsServer) throws IOException {
String testUsername = "testusername";
String testPassword = "testpassword";
String credentialDescription = "testDescription";
//test UsernamePasswordCredential
UsernamePasswordCredential testUPCredential = new UsernamePasswordCredential();
testUPCredential.setId("usernamepasswordcredentialTest-" + RandomStringUtils.randomAlphanumeric(24));
testUPCredential.setUsername(testUsername);
testUPCredential.setPassword(testPassword);
testUPCredential.setDescription(credentialDescription);

credentialOperations(jenkinsServer, testUPCredential);

//test SSHKeyCredential
SSHKeyCredential sshCredential = new SSHKeyCredential();
sshCredential.setId("sshusercredentialTest-" + RandomStringUtils.randomAlphanumeric(24));
sshCredential.setUsername(testUsername);
sshCredential.setPassphrase(testPassword);
sshCredential.setPrivateKeyType(SSHKeyCredential.PRIVATE_KEY_TYPES.DIRECT_ENTRY);
sshCredential.setPrivateKeyValue("testPrivateKeyContent");

credentialOperations(jenkinsServer, sshCredential);

//test certificate credential
CertificateCredential certificateCredential = new CertificateCredential();
certificateCredential.setId("certficateTest-" + RandomStringUtils.randomAlphanumeric(24));
certificateCredential.setCertificateSourceType(CertificateCredential.CERTIFICATE_SOURCE_TYPES.FILE_ON_MASTER);
certificateCredential.setCertificatePath("/tmp/test");
certificateCredential.setPassword("testpasssword");

credentialOperations(jenkinsServer, certificateCredential);

//test AppleDeveloperProfileCredential
AppleDeveloperProfileCredential appleDevProfile = new AppleDeveloperProfileCredential();
appleDevProfile.setId("appleProfileTest-" + RandomStringUtils.randomAlphanumeric(24));
appleDevProfile.setPassword(testPassword);
appleDevProfile.setDeveloperProfileContent("testprofile".getBytes());

credentialOperations(jenkinsServer, appleDevProfile);
}

private void credentialOperations(JenkinsServer jenkinsServer, Credential credential) throws IOException {
//create the credential
String credentialId = credential.getId();
jenkinsServer.createCredential(credential, false);

//check if has been created by listing
Map<String, Credential> credentials = jenkinsServer.listCredentials();
Credential found = credentials.get(credentialId);
assertNotNull(found);
assertEquals(credential.getTypeName(), found.getTypeName());

//compare fields
assertEquals(credentialId, found.getId());
assertNotNull(found.getDisplayName());

//update the credential
String updateDescription = "updatedDescription";
credential.setDescription(updateDescription);
jenkinsServer.updateCredential(credentialId, credential, false);

//verify it is updated
credentials = jenkinsServer.listCredentials();
found = credentials.get(credentialId);
assertEquals(updateDescription, found.getDescription());

//delete the credential
jenkinsServer.deleteCredential(credentialId, false);
credentials = jenkinsServer.listCredentials();
assertFalse(credentials.containsKey(credentialId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ public void getPluginsShouldReturn9ForJenkins20() {
}

@Test
public void getPluginsShouldReturn27ForJenkins1651() {
public void getPluginsShouldReturn28ForJenkins1651() {
JenkinsVersion jv = jenkinsServer.getVersion();
if (jv.isLessThan("1.651") && jv.isGreaterThan("1.651.3")) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be an or instead of an and?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aidenkeating not really sure TBH. I didn't write this test. Just update the name of the test as I added a new plugin

throw new SkipException("Not Version 1.651 (" + jv.toString() + ")");
}
assertThat(pluginManager.getPlugins()).hasSize(27);
assertThat(pluginManager.getPlugins()).hasSize(28);
}

private Plugin createPlugin(String shortName, String version) {
Expand Down Expand Up @@ -101,7 +101,7 @@ public void getPluginsShouldReturnTheListOfInstalledPluginsFor1651() {
// instead of maintaining at two locations.
//@formatter:off
Plugin[] expectedPlugins = {
createPlugin("token-macro", "1.12.1"),
createPlugin("token-macro", "1.12.1"),
createPlugin("translation", "1.10"),
createPlugin("testng-plugin", "1.10"),
createPlugin("matrix-project", "1.4.1"),
Expand All @@ -127,7 +127,8 @@ public void getPluginsShouldReturnTheListOfInstalledPluginsFor1651() {
createPlugin("throttle-concurrents", "1.9.0"),
createPlugin("subversion", "1.54"),
createPlugin("ssh-slaves", "1.9"),
createPlugin("cloudbees-folder", "5.12"),
createPlugin("cloudbees-folder", "5.12"),
createPlugin("xcode-plugin", "2.0.0"),
};
//@formatter:on
List<Plugin> plugins = pluginManager.getPlugins();
Expand Down
5 changes: 5 additions & 0 deletions jenkins-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
<artifactId>httpclient</artifactId>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
</dependency>

<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
Expand Down
147 changes: 109 additions & 38 deletions jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,32 @@

package com.offbytwo.jenkins;

import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBException;

import org.apache.http.HttpStatus;
import org.apache.http.client.HttpResponseException;
import org.apache.http.entity.ContentType;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.offbytwo.jenkins.client.JenkinsHttpClient;
import com.offbytwo.jenkins.client.util.EncodingUtils;
import com.offbytwo.jenkins.helper.JenkinsVersion;
import com.offbytwo.jenkins.model.Build;
import com.offbytwo.jenkins.model.Computer;
import com.offbytwo.jenkins.model.ComputerSet;
import com.offbytwo.jenkins.model.FolderJob;
import com.offbytwo.jenkins.model.Job;
import com.offbytwo.jenkins.model.JobConfiguration;
import com.offbytwo.jenkins.model.JobWithDetails;
import com.offbytwo.jenkins.model.LabelWithDetails;
import com.offbytwo.jenkins.model.MainView;
import com.offbytwo.jenkins.model.MavenJobWithDetails;
import com.offbytwo.jenkins.model.PluginManager;
import com.offbytwo.jenkins.model.Queue;
import com.offbytwo.jenkins.model.QueueItem;
import com.offbytwo.jenkins.model.QueueReference;
import com.offbytwo.jenkins.model.View;
import com.offbytwo.jenkins.model.*;
import com.offbytwo.jenkins.model.credentials.Credential;
import com.offbytwo.jenkins.model.credentials.CredentialManager;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpResponseException;
import org.apache.http.entity.ContentType;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.URI;
import java.rmi.server.ExportException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
* The main starting point for interacting with a Jenkins server.
Expand All @@ -52,6 +41,8 @@ public class JenkinsServer {

private final JenkinsHttpClient client;

private CredentialManager credentialManager;

/**
* Create a new Jenkins server reference given only the server address
*
Expand Down Expand Up @@ -934,24 +925,104 @@ private String toViewBaseUrl(FolderJob folder, String name) {
* @param jobName the fullName of the job.
* @return the path of the job including folders if present.
*/
private String parseFullName(String jobName)
{
private String parseFullName(String jobName) {
if (!jobName.contains("/")) {
return jobName;
}

List<String> foldersAndJob = Arrays.asList(jobName.split("/"));

String foldersAndJobName = "";

for (int i = 0; i < foldersAndJob.size(); i++) {
foldersAndJobName += foldersAndJob.get(i);
if (i != foldersAndJob.size() -1) {

if (i != foldersAndJob.size() - 1) {
foldersAndJobName += "/job/";
}
}

return foldersAndJobName;

}

/**
* List the credentials from the Jenkins server.
* @return a hash map of the credentials. The key is the id of each credential.
* @throws IOException
*/
public Map<String, Credential> listCredentials() throws IOException {
return this.getCredentialManager().listCredentials();
}

/**
* Create the given credential
* @param credential a credential instance
* @param crumbFlag
* @throws IOException
*/
public void createCredential(Credential credential, boolean crumbFlag) throws IOException {
this.getCredentialManager().createCredential(credential, crumbFlag);
}

/**
* Update an existing credential
* @param credentialId the id of the credential
* @param credential the updated credential instance
* @param crumbFlag
* @throws IOException
*/
public void updateCredential(String credentialId, Credential credential, boolean crumbFlag) throws IOException {
this.getCredentialManager().updateCredential(credentialId, credential, crumbFlag);
}

/**
* Delete an existing credential
* @param credentialId the id of the credential to delete
* @param crumbFlag
* @throws IOException
*/
public void deleteCredential(String credentialId, boolean crumbFlag) throws IOException {
this.getCredentialManager().deleteCredential(credentialId, crumbFlag);
}

/**
* Return the credentialManager instance. Will initialise it if it's never used before.
* @return the credentialManager instance
* @throws IOException
*/
private CredentialManager getCredentialManager() throws IOException {
if (this.credentialManager == null) {
Plugin credentialPlugin = findPluginWithName("credentials");
if (credentialPlugin == null) {
throw new ExportException("credential plugin is not installed");
}
String version = credentialPlugin.getVersion();
this.credentialManager = new CredentialManager(version, this.client);
}
return this.credentialManager;
}

/**
* Find a plugin that matches the given short name
* @param pluginShortName the short name of the plugin to find
* @return the pluin object that is found. Can be null if no match found.
* @throws IOException
*/
public Plugin findPluginWithName(final String pluginShortName) throws IOException {
List<Plugin> plugins = this.getPluginManager().getPlugins();
Object foundPlugin = CollectionUtils.find(plugins, new Predicate() {
@Override
public boolean evaluate(Object o) {
Plugin p = (Plugin) o;
if (p.getShortName().equalsIgnoreCase(pluginShortName)) {
return true;
} else {
return false;
}
}
});

return foundPlugin == null ? null : (Plugin) foundPlugin;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.offbytwo.jenkins.client;

public class FormBinaryField {
private String fileName;
private String contentType;
private byte[] content;

public FormBinaryField(String fileName, String contentType, byte[] content) {
this.fileName = fileName;
this.contentType = contentType;
this.content = content;
}

public String getFileName() {
return fileName;
}

public String getContentType() {
return contentType;
}

public byte[] getContent() {
return content;
}
}
Loading