diff options
author | S.Nishio <nishio@densan-labs.net> | 2016-02-07 16:46:06 +0900 |
---|---|---|
committer | S.Nishio <nishio@densan-labs.net> | 2016-02-07 16:46:06 +0900 |
commit | 77e9636ee92d8d5635be21b9598d5b37b58a7951 (patch) | |
tree | cc44795d13511885860b4372e255e758984f48ea /src/main/java/bitbucketpullrequestbuilder | |
parent | bcedbd3da1781bdf13db196f5854f945f112e27c (diff) | |
parent | 85a09f7392c50531365e89e94766a9caf3cac0f1 (diff) | |
download | bbprb-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')
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; |