Skip to content
Merged
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
7 changes: 6 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
<dependency>
<groupId>io.jenkins.tools.bom</groupId>
<artifactId>bom-${jenkins.baseline}.x</artifactId>
<version>5750.vec44cb_c78352</version>
<version>5804.v80587a_38d937</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand All @@ -54,6 +54,11 @@
<artifactId>gitlab-api</artifactId>
<version>6.2.0-111.vf174d6b_8c12e</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>
<version>1480.v2246fd131e83</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;

import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
Expand Down Expand Up @@ -119,17 +118,12 @@
if (serverUri.getHost() != null) {
builder.withHostname(serverUri.getHost());
}
StandardUsernameCredentials credentials = CredentialsMatchers.firstOrNull(
CredentialsProvider.lookupCredentials(
StandardUsernameCredentials.class,
context,
context instanceof Queue.Task
? ((Queue.Task) context).getDefaultAuthentication()
: ACL.SYSTEM,
builder.build()),
CredentialsMatchers.allOf(
CredentialsMatchers.withId(credentialsId),
CredentialsMatchers.instanceOf(StandardUsernameCredentials.class)));
StandardUsernameCredentials credentials = CredentialsProvider.findCredentialByIdInItem(
credentialsId,
StandardUsernameCredentials.class,
context,
context instanceof Queue.Task t ? t.getDefaultAuthentication2() : ACL.SYSTEM2,
builder.build());

Check warning on line 126 in src/main/java/io/jenkins/plugins/gitlabbranchsource/GitLabSCMBuilder.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 121-126 are not covered by tests
if (credentials instanceof SSHUserPrivateKey) {
return UriTemplate.buildFromTemplate(sshRemote).build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package io.jenkins.plugins.gitlabbranchsource.helpers;

import static com.cloudbees.plugins.credentials.CredentialsMatchers.withId;
import static com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials;
import static com.cloudbees.plugins.credentials.domains.URIRequirementBuilder.fromUri;

import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.damnhandy.uri.template.UriTemplate;
import com.damnhandy.uri.template.UriTemplateBuilder;
import com.damnhandy.uri.template.impl.Operator;
import hudson.ProxyConfiguration;
import hudson.model.Item;
import hudson.model.ItemGroup;
import hudson.security.ACL;
import hudson.security.AccessControlled;
import io.jenkins.plugins.gitlabserverconfig.credentials.GroupAccessToken;
import io.jenkins.plugins.gitlabserverconfig.credentials.PersonalAccessToken;
Expand Down Expand Up @@ -168,26 +165,28 @@

public static StandardCredentials getCredential(String credentialsId, String serverName, AccessControlled context) {
if (StringUtils.isNotBlank(credentialsId)) {
if (context instanceof ItemGroup) {
return CredentialsMatchers.firstOrNull(
lookupCredentials(
StandardCredentials.class,
(ItemGroup) context,
ACL.SYSTEM,
fromUri(StringUtils.defaultIfBlank(
getServerUrlFromName(serverName), GitLabServer.GITLAB_SERVER_URL))
.build()),
CredentialsMatchers.allOf(withId(credentialsId), GitLabServer.CREDENTIALS_MATCHER));
} else {
return CredentialsMatchers.firstOrNull(
lookupCredentials(
StandardCredentials.class,
(Item) context,
ACL.SYSTEM,
fromUri(StringUtils.defaultIfBlank(
getServerUrlFromName(serverName), GitLabServer.GITLAB_SERVER_URL))
.build()),
CredentialsMatchers.allOf(withId(credentialsId), GitLabServer.CREDENTIALS_MATCHER));
StandardCredentials c = null;
if (context instanceof ItemGroup<?> g) {

Check warning on line 169 in src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabHelper.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 169 is only partially covered, one branch is missing
c = CredentialsProvider.findCredentialByIdInItemGroup(
credentialsId,
StandardCredentials.class,
g,
null,
fromUri(StringUtils.defaultIfBlank(
getServerUrlFromName(serverName), GitLabServer.GITLAB_SERVER_URL))
.build());
} else if (context instanceof Item i) {
c = CredentialsProvider.findCredentialByIdInItem(
credentialsId,
StandardCredentials.class,
i,
null,
fromUri(StringUtils.defaultIfBlank(
getServerUrlFromName(serverName), GitLabServer.GITLAB_SERVER_URL))
.build());

Check warning on line 186 in src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabHelper.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 178-186 are not covered by tests
}
if (c != null && GitLabServer.CREDENTIALS_MATCHER.matches(c)) {

Check warning on line 188 in src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabHelper.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 188 is only partially covered, 3 branches are missing
return c;

Check warning on line 189 in src/main/java/io/jenkins/plugins/gitlabbranchsource/helpers/GitLabHelper.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 189 is not covered by tests
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.jenkins.plugins.gitlabserverconfig.servers;

import static com.cloudbees.plugins.credentials.CredentialsMatchers.withId;
import static com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials;
import static com.cloudbees.plugins.credentials.domains.URIRequirementBuilder.fromUri;
import static io.jenkins.plugins.gitlabbranchsource.helpers.GitLabHelper.getPrivateTokenAsPlainText;
import static io.jenkins.plugins.gitlabbranchsource.helpers.GitLabHelper.getProxyConfig;
Expand All @@ -14,7 +12,6 @@
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardCredentials;
import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
Expand All @@ -32,7 +29,6 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
Expand Down Expand Up @@ -281,401 +277,394 @@
}
return StringUtils.isBlank(credentialsId)
? null
: CredentialsMatchers.firstOrNull(
lookupCredentials(
StandardCredentials.class,
jenkins,
ACL.SYSTEM,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build()),
withId(credentialsId));
: CredentialsProvider.findCredentialByIdInItemGroup(
credentialsId,
StandardCredentials.class,
null,
null,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL)).build());
}

/**
* @param hooksRootUrl a custom root URL, to be used in hooks instead of
* {@link Jenkins#getRootUrl()}.
* Set to {@code null} for default behavior.
*/
@DataBoundSetter
public void setHooksRootUrl(String hooksRootUrl) {
this.hooksRootUrl = Util.fixEmptyAndTrim(hooksRootUrl);
}

/**
* @return the custom root URL, to be used in hooks instead of
* {@link Jenkins#getRootUrl()}.
* Can be either a root URL with its trailing slash, or {@code null}.
*/
@CheckForNull
public String getHooksRootUrl() {
return Util.ensureEndsWith(Util.fixEmptyAndTrim(hooksRootUrl), "/");
}

@DataBoundSetter
@Deprecated
public void setSecretToken(Secret token) {
this.secretToken = token;
}

@DataBoundSetter
public void setWebhookSecretCredentialsId(String token) {
this.webhookSecretCredentialsId = token;
}

public String getWebhookSecretCredentialsId() {
return webhookSecretCredentialsId;
}

/**
* Looks up for StringCredentials
*
* @return {@link StringCredentials}
*/
public StringCredentials getWebhookSecretCredentials(AccessControlled context) {
Jenkins jenkins = Jenkins.get();
if (context == null) {
jenkins.checkPermission(CredentialsProvider.USE_OWN);
return StringUtils.isBlank(webhookSecretCredentialsId)
? null
: CredentialsMatchers.firstOrNull(
lookupCredentials(
StringCredentials.class,
jenkins,
ACL.SYSTEM,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build()),
withId(webhookSecretCredentialsId));
: CredentialsProvider.findCredentialByIdInItemGroup(
webhookSecretCredentialsId,
StringCredentials.class,
null,
null,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build());
} else {
context.checkPermission(CredentialsProvider.USE_OWN);
if (context instanceof ItemGroup) {
if (context instanceof ItemGroup<?> g) {
return StringUtils.isBlank(webhookSecretCredentialsId)
? null
: CredentialsMatchers.firstOrNull(
lookupCredentials(
StringCredentials.class,
(ItemGroup) context,
ACL.SYSTEM,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build()),
withId(webhookSecretCredentialsId));
} else {
: CredentialsProvider.findCredentialByIdInItemGroup(
webhookSecretCredentialsId,
StringCredentials.class,
g,
null,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build());
} else if (context instanceof Item i) {
return StringUtils.isBlank(webhookSecretCredentialsId)
? null
: CredentialsMatchers.firstOrNull(
lookupCredentials(
StringCredentials.class,
(Item) context,
ACL.SYSTEM,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build()),
withId(webhookSecretCredentialsId));
: CredentialsProvider.findCredentialByIdInItem(
webhookSecretCredentialsId,
StringCredentials.class,
i,
null,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build());
} else {
return null;
}
}
}

/**
* {@inheritDoc}
*/
@Override
public DescriptorImpl getDescriptor() {
return (DescriptorImpl) super.getDescriptor();
}

@Deprecated
public Secret getSecretToken() {
return secretToken;
}

private StringCredentials getWebhookSecretCredentials(String webhookSecretCredentialsId) {
Jenkins jenkins = Jenkins.get();
return StringUtils.isBlank(webhookSecretCredentialsId)
? null
: CredentialsMatchers.firstOrNull(
lookupCredentials(
StringCredentials.class, jenkins, ACL.SYSTEM, new ArrayList<DomainRequirement>()),
withId(webhookSecretCredentialsId));
: CredentialsProvider.findCredentialByIdInItemGroup(
webhookSecretCredentialsId, StringCredentials.class, null, null, null);
}

public String getSecretTokenAsPlainText() {
StringCredentials credentials = getWebhookSecretCredentials(webhookSecretCredentialsId);
String secretToken = "";
if (credentials != null) {
secretToken = credentials.getSecret().getPlainText();
} else {
return null;
}
return secretToken;
}

/**
* Migrate webhook secret token to Jenkins credentials
*
* @return {@code true} if migration occurred, {@code false} otherwise
* @see GitLabServers#migrateWebhookSecretsToCredentials
*/
boolean migrateWebhookSecretCredentials() {
if (!StringUtils.isBlank(webhookSecretCredentialsId) || secretToken == null) {
return false;
}
final List<StringCredentials> credentials = CredentialsProvider.lookupCredentials(
StringCredentials.class, Jenkins.get(), ACL.SYSTEM, Collections.emptyList());
for (final StringCredentials cred : credentials) {
if (StringUtils.equals(secretToken.getPlainText(), Secret.toString(cred.getSecret()))) {
// If a credential has the same secret, use it.
webhookSecretCredentialsId = cred.getId();
break;
}
}
if (StringUtils.isBlank(webhookSecretCredentialsId)) {
// If we couldn't find any existing credentials, create new credential
final StringCredentials newCredentials = new StringCredentialsImpl(
CredentialsScope.GLOBAL,
null,
"Migrated from gitlab-branch-source-plugin webhook secret",
secretToken);
SystemCredentialsProvider.getInstance().getCredentials().add(newCredentials);
webhookSecretCredentialsId = newCredentials.getId();
}
secretToken = null;
return true;
}

/**
* Returns {@code true} if Jenkins should trigger a build immediately on a
* GitLab Web Hook trigger.
*
* @return {@code true} if Jenkins should trigger a build immediately on a
* GitLab Web Hook trigger.
*/
public boolean isImmediateHookTrigger() {
return immediateHookTrigger;
}

/**
* Data Bound Setter for immediate build on a GitLab Web Hook trigger.
*
* @param immediateHookTrigger {@code true} if and only if Jenkins should
* trigger a build immediately on a
* GitLab Web Hook trigger.
*/
@DataBoundSetter
public void setImmediateHookTrigger(boolean immediateHookTrigger) {
this.immediateHookTrigger = immediateHookTrigger;
}

/**
* Data Bound Setter for web hook trigger delay
*
* @param hookTriggerDelay Delay to be used for GitLab Web Hook build triggers.
* Set to {@code null} to use delay equal to GitLab
* cache timeout, which
* will avoid builds being not triggered due to GitLab
* caching.
*/
@DataBoundSetter
public void setHookTriggerDelay(String hookTriggerDelay) {
try {
this.hookTriggerDelay = Integer.parseInt(hookTriggerDelay);
} catch (NumberFormatException e) {
this.hookTriggerDelay = null;
}
}

/**
* @return Delay to be used for GitLab Web Hook build triggers.
* Can be either a root URL with its trailing slash, or {@code null}.
* Can be {@code null} to request delay to be equal to GitLab cache
* timeout.
*/
@CheckForNull
public Integer getHookTriggerDelay() {
return this.hookTriggerDelay;
}

/**
* Our descriptor.
*/
@Symbol("gitLabServer")
@Extension
public static class DescriptorImpl extends Descriptor<GitLabServer> {

/**
* Checks that the supplied URL is valid.
*
* @param serverUrl the URL to check.
* @return the validation results.
*/
@POST
public static FormValidation doCheckServerUrl(@QueryParameter String serverUrl) {
Jenkins.get().checkPermission(Jenkins.MANAGE);
try {
new URL(serverUrl);
} catch (MalformedURLException e) {
LOGGER.log(Level.SEVERE, String.format("Incorrect url: %s", serverUrl));
return FormValidation.error("Malformed url (%s)", e.getMessage());
}
if (GITLAB_SERVER_URL.equals(serverUrl)) {
LOGGER.log(Level.FINEST, String.format("Community version of GitLab: %s", serverUrl));
}
GitLabApi gitLabApi = new GitLabApi(serverUrl, "", null, getProxyConfig(serverUrl));
try {
gitLabApi.getProjectApi().getProjects(1, 1);
return FormValidation.ok();
} catch (GitLabApiException e) {
LOGGER.log(Level.FINEST, String.format("Invalid GitLab Server Url: %s", serverUrl));
return FormValidation.error(Messages.GitLabServer_invalidUrl(serverUrl));
}
}

/**
* Checks that the supplied URL looks like a valid Jenkins root URL.
*
* @param hooksRootUrl the URL to check.
* @return the validation results.
*/
public static FormValidation doCheckHooksRootUrl(@QueryParameter String hooksRootUrl) {
if (StringUtils.isBlank(hooksRootUrl)) {
return FormValidation.ok();
}
try {
new URL(hooksRootUrl);
} catch (MalformedURLException e) {
LOGGER.log(Level.FINEST, "Malformed hooks root URL: {0}", hooksRootUrl);
return FormValidation.error("Malformed url (%s)", e.getMessage());
}
if (hooksRootUrl.endsWith("/post")
|| hooksRootUrl.contains("/gitlab-webhook")
|| hooksRootUrl.contains("/gitlab-systemhook")) {
LOGGER.log(Level.FINEST, "Dubious hooks root URL: {0}", hooksRootUrl);
return FormValidation.warning("This looks like a full webhook URL, it should only be a root URL.");
}
return FormValidation.ok();
}

/**
* Checks that the supplied hook trigger delay is valid.
*
* @param hookTriggerDelay the delay to be checked.
* @return the validation results.
*/
public static FormValidation doCheckHookTriggerDelay(@QueryParameter String hookTriggerDelay) {
try {
if (!hookTriggerDelay.isEmpty()) {
Integer.parseInt(hookTriggerDelay);
}
return FormValidation.ok();
} catch (NumberFormatException e) {
LOGGER.log(Level.FINEST, "Invalid hook trigger delay: {0}", hookTriggerDelay);
return FormValidation.error("Invalid hook trigger delay (%s)", e.getMessage());
}
}

@NonNull
@Override
public String getDisplayName() {
return Messages.GitLabServer_displayName();
}

@RequirePOST
@Restricted(DoNotUse.class)
@SuppressWarnings("unused")
public FormValidation doTestConnection(@QueryParameter String serverUrl, @QueryParameter String credentialsId) {
StandardCredentials credentials = getCredentials(serverUrl, credentialsId);
String privateToken = getPrivateTokenAsPlainText(credentials);
if (privateToken.equals(EMPTY_TOKEN)) {
GitLabApi gitLabApi = new GitLabApi(serverUrl, EMPTY_TOKEN, null, getProxyConfig(serverUrl));
try {
/*
* In order to validate a GitLab Server without personal access token,
* we are fetching 1 project from the GitLab Server. If no project exists,
* it returns an empty list. If no server exists at the specified endpoint,
* it raises GitLabAPIException.
*/
gitLabApi.getProjectApi().getProjects(1, 1);
return FormValidation.ok("Valid GitLab Server but no credentials specified");
} catch (GitLabApiException e) {
LOGGER.log(Level.SEVERE, "Invalid GitLab Server Url");
return FormValidation.errorWithMarkup(
Messages.GitLabServer_credentialsNotResolved(Util.escape(credentialsId)));
}
} else {
GitLabApi gitLabApi = new GitLabApi(serverUrl, privateToken, null, getProxyConfig(serverUrl));
try {
User user = gitLabApi.getUserApi().getCurrentUser();
LOGGER.log(
Level.FINEST,
String.format("Connection established with the GitLab Server for %s", user.getUsername()));
return FormValidation.ok(String.format("Credentials verified for user %s", user.getUsername()));
} catch (GitLabApiException e) {
LOGGER.log(
Level.SEVERE, String.format("Failed to connect with GitLab Server - %s", e.getMessage()));
return FormValidation.error(e, Messages.GitLabServer_failedValidation(Util.escape(e.getMessage())));
}
}
}

/**
* Stapler form completion.
*
* @param serverUrl the server URL.
* @param credentialsId the credentials Id
* @return the available credentials.
*/
@Restricted(NoExternalUse.class) // stapler
@SuppressWarnings("unused")
public ListBoxModel doFillCredentialsIdItems(
@QueryParameter String serverUrl, @QueryParameter String credentialsId) {
Jenkins jenkins = Jenkins.get();
if (!jenkins.hasPermission(Jenkins.MANAGE)) {
return new StandardListBoxModel().includeCurrentValue(credentialsId);
}
return new StandardListBoxModel()
.includeEmptyValue()
.includeMatchingAs(
ACL.SYSTEM,
jenkins,
StandardCredentials.class,
fromUri(serverUrl).build(),
CREDENTIALS_MATCHER);
}

/**
* Stapler form completion.
*
* @param webhookSecretCredentialsId the webhook secret credentials Id
* @return the available credentials.
*/
@Restricted(NoExternalUse.class) // stapler
@SuppressWarnings("unused")
public ListBoxModel doFillWebhookSecretCredentialsIdItems(
@QueryParameter String serverUrl, @QueryParameter String webhookSecretCredentialsId) {
Jenkins jenkins = Jenkins.get();
if (!jenkins.hasPermission(Jenkins.MANAGE)) {
return new StandardListBoxModel().includeCurrentValue(webhookSecretCredentialsId);
}
return new StandardListBoxModel()
.includeEmptyValue()
.includeMatchingAs(
ACL.SYSTEM,
jenkins,
StringCredentials.class,
fromUri(serverUrl).build(),
WEBHOOK_SECRET_CREDENTIALS_MATCHER);
}

private StandardCredentials getCredentials(String serverUrl, String credentialsId) {
Jenkins jenkins = Jenkins.get();
jenkins.checkPermission(Jenkins.MANAGE);
return StringUtils.isBlank(credentialsId)
? null
: CredentialsMatchers.firstOrNull(
lookupCredentials(
StandardCredentials.class,
jenkins,
ACL.SYSTEM,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build()),
withId(credentialsId));
: CredentialsProvider.findCredentialByIdInItemGroup(
credentialsId,
StandardCredentials.class,
null,
null,
fromUri(defaultIfBlank(serverUrl, GITLAB_SERVER_URL))
.build());

Check warning on line 667 in src/main/java/io/jenkins/plugins/gitlabserverconfig/servers/GitLabServer.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 280-667 are not covered by tests
}
}
}
Loading