aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/bitbucketpullrequestbuilder
diff options
context:
space:
mode:
authorS.Nishio <nishio@densan-labs.net>2016-02-07 16:46:06 +0900
committerS.Nishio <nishio@densan-labs.net>2016-02-07 16:46:06 +0900
commit77e9636ee92d8d5635be21b9598d5b37b58a7951 (patch)
treecc44795d13511885860b4372e255e758984f48ea /src/main/java/bitbucketpullrequestbuilder
parentbcedbd3da1781bdf13db196f5854f945f112e27c (diff)
parent85a09f7392c50531365e89e94766a9caf3cac0f1 (diff)
downloadbbprb-77e9636ee92d8d5635be21b9598d5b37b58a7951.tar.gz
Merge pull request #74 from maxvodo/master
Add support for credentials from the Jenkins credentials store
Diffstat (limited to 'src/main/java/bitbucketpullrequestbuilder')
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java21
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java17
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java57
-rw-r--r--src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java94
4 files changed, 157 insertions, 32 deletions
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
index cd9345c..2375ec7 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketBuildTrigger.java
@@ -1,12 +1,17 @@
package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
import antlr.ANTLRException;
+import com.cloudbees.plugins.credentials.CredentialsProvider;
+import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
+import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
+import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
import hudson.Extension;
import hudson.model.*;
import hudson.model.queue.QueueTaskFuture;
import hudson.plugins.git.RevisionParameterAction;
import hudson.triggers.Trigger;
import hudson.triggers.TriggerDescriptor;
+import hudson.util.ListBoxModel;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
@@ -17,6 +22,8 @@ import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
+import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf;
+
/**
* Created by nishio
*/
@@ -24,6 +31,7 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
private static final Logger logger = Logger.getLogger(BitbucketBuildTrigger.class.getName());
private final String projectPath;
private final String cron;
+ private final String credentialsId;
private final String username;
private final String password;
private final String repositoryOwner;
@@ -45,6 +53,7 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
public BitbucketBuildTrigger(
String projectPath,
String cron,
+ String credentialsId,
String username,
String password,
String repositoryOwner,
@@ -60,6 +69,7 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
super(cron);
this.projectPath = projectPath;
this.cron = cron;
+ this.credentialsId = credentialsId;
this.username = username;
this.password = password;
this.repositoryOwner = repositoryOwner;
@@ -81,6 +91,10 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
return this.cron;
}
+ public String getCredentialsId() {
+ return credentialsId;
+ }
+
public String getUsername() {
return username;
}
@@ -208,5 +222,12 @@ public class BitbucketBuildTrigger extends Trigger<AbstractProject<?, ?>> {
save();
return super.configure(req, json);
}
+
+ public ListBoxModel doFillCredentialsIdItems() {
+ return new StandardListBoxModel()
+ .withEmptySelection()
+ .withMatching(instanceOf(UsernamePasswordCredentials.class),
+ CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class));
+ }
}
}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java
index c5d4159..d337796 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketPullRequestsBuilder.java
@@ -2,10 +2,15 @@ package bitbucketpullrequestbuilder.bitbucketpullrequestbuilder;
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest;
import hudson.model.AbstractProject;
+
+import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Collection;
+import java.util.logging.Level;
import java.util.logging.Logger;
+
import org.apache.commons.codec.binary.Hex;
/**
@@ -54,12 +59,20 @@ public class BitbucketPullRequestsBuilder {
return this.project;
}
+ /**
+ * Return MD5 hashed full project name or full project name, if MD5 hash provider inaccessible
+ * @return unique project id
+ */
public String getProjectId() {
try {
final MessageDigest MD5 = MessageDigest.getInstance("MD5");
return new String(Hex.encodeHex(MD5.digest(this.project.getFullName().getBytes("UTF-8"))));
- } catch (Exception exc) {
- logger.severe(exc.toString());
+ } catch (NoSuchAlgorithmException exc) {
+ logger.log(Level.WARNING, "Failed to produce hash", exc);
+ exc.printStackTrace();
+ } catch (UnsupportedEncodingException exc) {
+ logger.log(Level.WARNING, "Failed to produce hash", exc);
+ exc.printStackTrace();
}
return this.project.getFullName();
}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
index 6736ee9..9b92775 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/BitbucketRepository.java
@@ -9,16 +9,26 @@ import java.util.logging.Logger;
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.ApiClient;
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.BuildState;
import bitbucketpullrequestbuilder.bitbucketpullrequestbuilder.bitbucket.Pullrequest;
+
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
+import com.cloudbees.plugins.credentials.CredentialsMatchers;
+import com.cloudbees.plugins.credentials.CredentialsProvider;
+import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
+import com.cloudbees.plugins.credentials.common.UsernamePasswordCredentials;
+
import jenkins.model.Jenkins;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.SCMSourceOwner;
import jenkins.scm.api.SCMSourceOwners;
+
import org.apache.commons.lang.StringUtils;
+import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf;
+
/**
* Created by nishio
*/
@@ -34,26 +44,46 @@ public class BitbucketRepository {
private BitbucketPullRequestsBuilder builder;
private BitbucketBuildTrigger trigger;
private ApiClient client;
-
+
public BitbucketRepository(String projectPath, BitbucketPullRequestsBuilder builder) {
this.projectPath = projectPath;
this.builder = builder;
}
public void init() {
- this.init(null);
+ this.init(null, null);
+ }
+
+ public <T extends ApiClient.HttpClientFactory> void init(T httpFactory) {
+ this.init(null, httpFactory);
}
public void init(ApiClient client) {
+ this.init(client, null);
+ }
+
+ public <T extends ApiClient.HttpClientFactory> void init(ApiClient client, T httpFactory) {
this.trigger = this.builder.getTrigger();
- this.client = (client == null) ? new ApiClient(
- trigger.getUsername(),
- trigger.getPassword(),
+
+ if (client == null) {
+ String username = trigger.getUsername();
+ String password = trigger.getPassword();
+ StandardUsernamePasswordCredentials credentials = getCredentials(trigger.getCredentialsId());
+ if (credentials != null) {
+ username = credentials.getUsername();
+ password = credentials.getPassword().getPlainText();
+ }
+ this.client = new ApiClient(
+ username,
+ password,
trigger.getRepositoryOwner(),
trigger.getRepositoryName(),
trigger.getCiKey(),
- trigger.getCiName()
- ) : client;
+ trigger.getCiName(),
+ httpFactory
+ );
+
+ } else this.client = client;
}
public Collection<Pullrequest> getTargetPullRequests() {
@@ -67,6 +97,10 @@ public class BitbucketRepository {
}
return targetPullRequests;
}
+
+ public ApiClient getClient() {
+ return this.client;
+ }
public void addFutureBuildTasks(Collection<Pullrequest> pullRequests) {
for(Pullrequest pullRequest : pullRequests) {
@@ -267,4 +301,13 @@ public class BitbucketRepository {
return filter.approved(cause);
}
+
+ private StandardUsernamePasswordCredentials getCredentials(String credentialsId) {
+ if (null == credentialsId) return null;
+ return CredentialsMatchers
+ .firstOrNull(
+ CredentialsProvider.lookupCredentials(StandardUsernamePasswordCredentials.class),
+ CredentialsMatchers.allOf(CredentialsMatchers.withId(credentialsId),
+ instanceOf(UsernamePasswordCredentials.class)));
+ }
}
diff --git a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java
index e635f65..19bff34 100644
--- a/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java
+++ b/src/main/java/bitbucketpullrequestbuilder/bitbucketpullrequestbuilder/bitbucket/ApiClient.java
@@ -16,8 +16,12 @@ import java.util.logging.Logger;
import jenkins.model.Jenkins;
import hudson.ProxyConfiguration;
+
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import org.apache.commons.codec.binary.Hex;
import org.apache.commons.httpclient.methods.PutMethod;
-import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.util.EncodingUtil;
/**
@@ -27,19 +31,54 @@ public class ApiClient {
private static final Logger logger = Logger.getLogger(ApiClient.class.getName());
private static final String V1_API_BASE_URL = "https://bitbucket.org/api/1.0/repositories/";
private static final String V2_API_BASE_URL = "https://bitbucket.org/api/2.0/repositories/";
- private static final String COMPUTED_KEY_FORMAT = "%s-%s";
+ private static final String COMPUTED_KEY_FORMAT = "%s-%s";
private String owner;
private String repositoryName;
private Credentials credentials;
private String key;
private String name;
+ private HttpClientFactory factory;
+
+ public static final byte MAX_KEY_SIZE_BB_API = 40;
+
+ public static class HttpClientFactory {
+ public static final HttpClientFactory INSTANCE = new HttpClientFactory();
+
+ public HttpClient getInstanceHttpClient() {
+ HttpClient client = new HttpClient();
+ if (Jenkins.getInstance() == null) return client;
+
+ ProxyConfiguration proxy = Jenkins.getInstance().proxy;
+ if (proxy == null) return client;
- public ApiClient(String username, String password, String owner, String repositoryName, String key, String name) {
+ logger.log(Level.INFO, "Jenkins proxy: {0}:{1}", new Object[]{ proxy.name, proxy.port });
+ client.getHostConfiguration().setProxy(proxy.name, proxy.port);
+ String username = proxy.getUserName();
+ String password = proxy.getPassword();
+
+ // Consider it to be passed if username specified. Sufficient?
+ if (username != null && !"".equals(username.trim())) {
+ logger.log(Level.INFO, "Using proxy authentication (user={0})", username);
+ client.getState().setProxyCredentials(AuthScope.ANY,
+ new UsernamePasswordCredentials(username, password));
+ }
+
+ return client;
+ }
+ }
+
+ public <T extends HttpClientFactory> ApiClient(
+ String username, String password,
+ String owner, String repositoryName,
+ String key, String name,
+ T httpFactory
+ ) {
this.credentials = new UsernamePasswordCredentials(username, password);
this.owner = owner;
this.repositoryName = repositoryName;
this.key = key;
- this.name = name;
+ this.name = name;
+ this.factory = httpFactory != null ? httpFactory : HttpClientFactory.INSTANCE;
}
public List<Pullrequest> getPullRequests() {
@@ -47,6 +86,7 @@ public class ApiClient {
return parse(get(v2("/pullrequests/")), Pullrequest.Response.class).getPullrequests();
} catch(Exception e) {
logger.log(Level.WARNING, "invalid pull request response.", e);
+ e.printStackTrace();
}
return Collections.EMPTY_LIST;
}
@@ -56,6 +96,7 @@ public class ApiClient {
return parse(get(v1("/pullrequests/" + pullRequestId + "/comments")), new TypeReference<List<Pullrequest.Comment>>() {});
} catch(Exception e) {
logger.log(Level.WARNING, "invalid pull request response.", e);
+ e.printStackTrace();
}
return Collections.EMPTY_LIST;
}
@@ -64,8 +105,29 @@ public class ApiClient {
return this.name;
}
+ private static MessageDigest SHA1 = null;
+
+ /**
+ * Retrun
+ * @param keyExPart
+ * @return key parameter for call BitBucket API
+ */
private String computeAPIKey(String keyExPart) {
- return String.format(COMPUTED_KEY_FORMAT, this.key, keyExPart);
+ String computedKey = String.format(COMPUTED_KEY_FORMAT, this.key, keyExPart);
+
+ if (computedKey.length() > MAX_KEY_SIZE_BB_API) {
+ try {
+ if (SHA1 == null) SHA1 = MessageDigest.getInstance("SHA1");
+ return new String(Hex.encodeHex(SHA1.digest(computedKey.getBytes("UTF-8"))));
+ } catch(NoSuchAlgorithmException e) {
+ logger.log(Level.WARNING, "Failed to create hash provider", e);
+ e.printStackTrace();
+ } catch (UnsupportedEncodingException e) {
+ logger.log(Level.WARNING, "Failed to create hash provider", e);
+ e.printStackTrace();
+ }
+ }
+ return (computedKey.length() <= MAX_KEY_SIZE_BB_API) ? computedKey : computedKey.substring(0, MAX_KEY_SIZE_BB_API);
}
public String buildStatusKey(String bsKey) {
@@ -112,6 +174,7 @@ public class ApiClient {
return parse(post(v2("/pullrequests/" + pullRequestId + "/approve"),
new NameValuePair[]{}), Pullrequest.Participant.class);
} catch (IOException e) {
+ logger.log(Level.WARNING, "Invalid pull request approval response.", e);
e.printStackTrace();
}
return null;
@@ -131,24 +194,7 @@ public class ApiClient {
}
private HttpClient getHttpClient() {
- HttpClient client = new HttpClient();
- if (Jenkins.getInstance() == null) return client;
-
- ProxyConfiguration proxy = Jenkins.getInstance().proxy;
- if (proxy == null) return client;
-
- logger.info("Jenkins proxy: " + proxy.name + ":" + proxy.port);
- client.getHostConfiguration().setProxy(proxy.name, proxy.port);
- String username = proxy.getUserName();
- String password = proxy.getPassword();
-
- // Consider it to be passed if username specified. Sufficient?
- if (username != null && !"".equals(username.trim())) {
- logger.info("Using proxy authentication (user=" + username + ")");
- client.getState().setProxyCredentials(AuthScope.ANY,
- new UsernamePasswordCredentials(username, password));
- }
- return client;
+ return this.factory.getInstanceHttpClient();
}
private String v1(String url) {
@@ -193,8 +239,10 @@ public class ApiClient {
client.executeMethod(req);
return req.getResponseBodyAsString();
} catch (HttpException e) {
+ logger.log(Level.WARNING, "Failed to send request.", e);
e.printStackTrace();
} catch (IOException e) {
+ logger.log(Level.WARNING, "Failed to send request.", e);
e.printStackTrace();
}
return null;