From a3f2e95cd40854a605fa7e835230294ff1f3cc42 Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Mon, 18 May 2020 18:46:51 -0400 Subject: [PATCH 1/3] =?UTF-8?q?GitHubAppCredentials.writeReplace=20?= =?UTF-8?q?=E2=86=92=20UsernamePasswordCredentialsImpl?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github_branch_source/GitHubAppCredentials.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java index 0c76d8a99..c6f0cd142 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java @@ -3,11 +3,13 @@ import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials; +import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; import hudson.Util; +import hudson.remoting.Channel; import hudson.util.FormValidation; import hudson.util.ListBoxModel; import hudson.util.Secret; @@ -165,6 +167,13 @@ public String getUsername() { return appID; } + private Object writeReplace() { + if (/* XStream */Channel.current() == null) { + return this; + } + return new UsernamePasswordCredentialsImpl(getScope(), getId(), getDescription(), getUsername(), getPassword().getPlainText()); + } + /** * {@inheritDoc} */ From 39e4538ba7b1dbf4c4ce88085257c91f8e9d5a9d Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Tue, 19 May 2020 11:48:51 -0400 Subject: [PATCH 2/3] Rather replace to a GitHubAppCredentials clone incl. transients --- .../GitHubAppCredentials.java | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java index c6f0cd142..0cf4b543f 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java @@ -3,7 +3,6 @@ import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials; -import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; @@ -14,6 +13,7 @@ import hudson.util.ListBoxModel; import hudson.util.Secret; import java.io.IOException; +import java.io.Serializable; import java.util.List; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -171,7 +171,42 @@ private Object writeReplace() { if (/* XStream */Channel.current() == null) { return this; } - return new UsernamePasswordCredentialsImpl(getScope(), getId(), getDescription(), getUsername(), getPassword().getPlainText()); + return new Replacer(this); + } + + private static final class Replacer implements Serializable { + + private final CredentialsScope scope; + private final String id; + private final String description; + private final String appID; + private final Secret privateKey; + private final String apiUri; + private final String owner; + private final String cachedToken; + private final long tokenCacheTime; + + Replacer(GitHubAppCredentials onMaster) { + scope = onMaster.getScope(); + id = onMaster.getId(); + description = onMaster.getDescription(); + appID = onMaster.appID; + privateKey = onMaster.privateKey; + apiUri = onMaster.apiUri; + owner = onMaster.owner; + cachedToken = onMaster.cachedToken; + tokenCacheTime = onMaster.tokenCacheTime; + } + + private Object readResolve() { + GitHubAppCredentials clone = new GitHubAppCredentials(scope, id, description, appID, privateKey); + clone.apiUri = apiUri; + clone.owner = owner; + clone.cachedToken = cachedToken; + clone.tokenCacheTime = tokenCacheTime; + return clone; + } + } /** From ebf19bed615c486039fc34f6cf050fac1a2b394f Mon Sep 17 00:00:00 2001 From: Jesse Glick Date: Thu, 21 May 2020 14:28:41 -0400 Subject: [PATCH 3/3] Javadoc --- .../github_branch_source/GitHubAppCredentials.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java index 0cf4b543f..86da634c1 100644 --- a/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java +++ b/src/main/java/org/jenkinsci/plugins/github_branch_source/GitHubAppCredentials.java @@ -1,6 +1,7 @@ package org.jenkinsci.plugins.github_branch_source; import com.cloudbees.plugins.credentials.CredentialsScope; +import com.cloudbees.plugins.credentials.CredentialsSnapshotTaker; import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials; import edu.umd.cs.findbugs.annotations.CheckForNull; @@ -167,7 +168,17 @@ public String getUsername() { return appID; } - private Object writeReplace() { + /** + * Ensures that the credentials state as serialized via Remoting to an agent includes fields which are {@code transient} for purposes of XStream. + * This provides a ~2× performance improvement over reconstructing the object without that state, + * in the normal case that {@link #cachedToken} is valid and will remain valid for the brief time that elapses before the agent calls {@link #getPassword}: + *
    + *
  • We do not need to make API calls to GitHub to obtain a new token. + *
  • We can avoid the considerable amount of class loading associated with the JWT library, Jackson data binding, Bouncy Castle, etc. + *
+ * @see CredentialsSnapshotTaker + */ + private Object writeReplace() { if (/* XStream */Channel.current() == null) { return this; }