Skip to content

Commit

Permalink
Prevent infinite recursion when migrating secrets to credentials whil…
Browse files Browse the repository at this point in the history
…e loading GitLab server configuration (#450)
  • Loading branch information
dwnusbaum authored Oct 15, 2024
1 parent 6f19df3 commit 41fbc8b
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -407,17 +407,16 @@ public String getSecretTokenAsPlainText() {
return secretToken;
}

private Object readResolve() {
if (StringUtils.isBlank(webhookSecretCredentialsId) && secretToken != null) {
migrateWebhookSecretCredentials();
}
return this;
}

/**
* Migrate webhook secret token to Jenkins credentials
*
* @return {@code true} if migration occurred, {@code false} otherwise
* @see GitLabServers#migrateWebhookSecretsToCredentials
*/
private void migrateWebhookSecretCredentials() {
boolean migrateWebhookSecretCredentials() {
if (!StringUtils.isBlank(webhookSecretCredentialsId) || secretToken == null) {

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

View check run for this annotation

ci.jenkins.io / Code Coverage

Partially covered line

Line 417 is only partially covered, one branch is missing
return false;
}
final List<StringCredentials> credentials = CredentialsProvider.lookupCredentials(
StringCredentials.class, Jenkins.get(), ACL.SYSTEM, Collections.emptyList());
for (final StringCredentials cred : credentials) {
Expand All @@ -438,6 +437,7 @@ private void migrateWebhookSecretCredentials() {
webhookSecretCredentialsId = newCredentials.getId();
}
secretToken = null;
return true;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.Extension;
import hudson.ExtensionList;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.model.Descriptor;
import hudson.model.PersistentDescriptor;
import hudson.security.Permission;
Expand Down Expand Up @@ -196,4 +198,16 @@ public GitLabServer findServer(@CheckForNull String serverName) {
.findAny()
.orElse(null);
}

@Initializer(after = InitMilestone.EXTENSIONS_AUGMENTED)
public static void migrateWebhookSecretsToCredentials() {
boolean modified = false;
var servers = GitLabServers.get();
for (GitLabServer server : servers.getServers()) {
modified |= server.migrateWebhookSecretCredentials();
}
if (modified) {
servers.save();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.jenkins.plugins.gitlabserverconfig.servers;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;

import com.cloudbees.plugins.credentials.Credentials;
import com.cloudbees.plugins.credentials.CredentialsMatchers;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.domains.DomainRequirement;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import hudson.diagnosis.OldDataMonitor;
import hudson.model.ItemGroup;
import hudson.security.ACL;
import java.util.List;
import java.util.logging.Level;
import jenkins.model.Jenkins;
import org.acegisecurity.Authentication;
import org.jenkinsci.plugins.plaincredentials.StringCredentials;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;
import org.jvnet.hudson.test.LoggerRule;
import org.jvnet.hudson.test.TestExtension;
import org.jvnet.hudson.test.recipes.LocalData;

public class GitLabServersTest {
@Rule
public LoggerRule logger =
new LoggerRule().record(OldDataMonitor.class, Level.FINE).capture(50);

@Rule
public JenkinsRule j = new JenkinsRule();

@LocalData
@Test
public void migrationToCredentials() throws Throwable {
// LocalData creating using the following:
/*
GitLabServer server = new GitLabServer("http://localhost", "my-server", null);
server.setSecretToken(Secret.fromString("s3cr3t!"));
GitLabServers.get().addServer(server);
*/
var server = GitLabServers.get().getServers().stream()
.filter(s -> s.getName().equals("my-server"))
.findFirst()
.orElseThrow();
var credentialsId = server.getWebhookSecretCredentialsId();
var credentials = CredentialsMatchers.filter(
CredentialsProvider.lookupCredentialsInItemGroup(StringCredentials.class, Jenkins.get(), ACL.SYSTEM2),
CredentialsMatchers.withId(credentialsId));
assertThat(credentials, hasSize(1));
assertThat(credentials.get(0).getSecret().getPlainText(), equalTo("s3cr3t!"));
assertThat(
logger.getMessages(), not(hasItem(containsString("Trouble loading " + GitLabServers.class.getName()))));
}

@TestExtension("migrationToCredentials")
public static class CredentialsProviderThatRequiresDescriptorLookup extends CredentialsProvider {
@Override
public <C extends Credentials> List<C> getCredentials(
@NonNull Class<C> type,
@Nullable ItemGroup itemGroup,
@Nullable Authentication authentication,
@NonNull List<DomainRequirement> domainRequirements) {
// Prior to fix, this caused the GitLabServer migration code to recurse infinitely, causing problems when
// starting Jenkins.
// In practice this was caused by a lookup of another descriptor type, but I am using GitLabServers for
// clarity.
Jenkins.get().getDescriptor(GitLabServers.class);
return List.of();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version='1.1' encoding='UTF-8'?>
<io.jenkins.plugins.gitlabserverconfig.servers.GitLabServers>
<servers>
<io.jenkins.plugins.gitlabserverconfig.servers.GitLabServer>
<name>default</name>
<serverUrl>https://gitlab.com</serverUrl>
<manageWebHooks>false</manageWebHooks>
<manageSystemHooks>false</manageSystemHooks>
<credentialsId></credentialsId>
<webhookSecretCredentialsId></webhookSecretCredentialsId>
<immediateHookTrigger>false</immediateHookTrigger>
</io.jenkins.plugins.gitlabserverconfig.servers.GitLabServer>
<io.jenkins.plugins.gitlabserverconfig.servers.GitLabServer>
<name>my-server</name>
<serverUrl>http://localhost</serverUrl>
<manageWebHooks>false</manageWebHooks>
<manageSystemHooks>false</manageSystemHooks>
<secretToken>s3cr3t!</secretToken> <!-- Manually modified to be plain text rather than the encrypted value so we do not have to store secret.key as a test resource, which makes security scanners unhappy. -->
<webhookSecretCredentialsId></webhookSecretCredentialsId>
<immediateHookTrigger>false</immediateHookTrigger>
</io.jenkins.plugins.gitlabserverconfig.servers.GitLabServer>
</servers>
</io.jenkins.plugins.gitlabserverconfig.servers.GitLabServers>

0 comments on commit 41fbc8b

Please sign in to comment.