From 3685c380ffe7e52fab0fe7e5211285a11b35f741 Mon Sep 17 00:00:00 2001 From: Funke Date: Tue, 20 Jun 2017 15:28:55 +0200 Subject: [PATCH 1/5] added support for AdditionalCredentials to SubversionSCMSource and fixed issues with externals - moved AddedCredentials to separate file (was nested class before) - issues with ISVNAuthentication and externals in multibranch jobs should be fixed now, when specifying additional credentials --- .../hudson/scm/AdditionalCredentials.java | 252 ++++++++++++++++++ ...dentialsSVNAuthenticationProviderImpl.java | 2 +- src/main/java/hudson/scm/SubversionSCM.java | 177 ++++-------- .../impl/subversion/SubversionSCMSource.java | 47 +++- .../SubversionSCMSource/config-detail.jelly | 3 + .../hudson/scm/CredentialsExternalsTest.java | 2 +- 6 files changed, 348 insertions(+), 135 deletions(-) create mode 100644 src/main/java/hudson/scm/AdditionalCredentials.java diff --git a/src/main/java/hudson/scm/AdditionalCredentials.java b/src/main/java/hudson/scm/AdditionalCredentials.java new file mode 100644 index 000000000..fd179b0f5 --- /dev/null +++ b/src/main/java/hudson/scm/AdditionalCredentials.java @@ -0,0 +1,252 @@ +package hudson.scm; + +import static hudson.Util.fixEmptyAndTrim; +import static hudson.scm.PollingResult.BUILD_NOW; +import static hudson.scm.PollingResult.NO_CHANGES; +import static java.util.logging.Level.FINE; +import static java.util.logging.Level.WARNING; + +import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; +import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey; +import com.cloudbees.plugins.credentials.Credentials; +import com.cloudbees.plugins.credentials.CredentialsMatchers; +import com.cloudbees.plugins.credentials.CredentialsNameProvider; +import com.cloudbees.plugins.credentials.CredentialsProvider; +import com.cloudbees.plugins.credentials.CredentialsScope; +import com.cloudbees.plugins.credentials.CredentialsStore; +import com.cloudbees.plugins.credentials.common.StandardCertificateCredentials; +import com.cloudbees.plugins.credentials.common.StandardCredentials; +import com.cloudbees.plugins.credentials.common.StandardListBoxModel; +import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; +import com.cloudbees.plugins.credentials.domains.Domain; +import com.cloudbees.plugins.credentials.domains.DomainRequirement; +import com.cloudbees.plugins.credentials.domains.DomainSpecification; +import com.cloudbees.plugins.credentials.domains.HostnameRequirement; +import com.cloudbees.plugins.credentials.domains.HostnameSpecification; +import com.cloudbees.plugins.credentials.domains.SchemeRequirement; +import com.cloudbees.plugins.credentials.domains.SchemeSpecification; +import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder; +import com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl; +import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; +import edu.umd.cs.findbugs.annotations.CheckForNull; +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.BulkChange; +import hudson.EnvVars; +import hudson.Extension; +import hudson.FilePath; +import hudson.FilePath.FileCallable; +import hudson.Launcher; +import hudson.Util; +import hudson.init.InitMilestone; +import hudson.model.*; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.UnsupportedCharsetException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.WeakHashMap; + +import hudson.security.ACL; +import hudson.util.ListBoxModel; +import jenkins.model.Jenkins; +import hudson.remoting.Channel; +import hudson.remoting.VirtualChannel; +import hudson.scm.UserProvidedCredential.AuthenticationManagerImpl; +import hudson.scm.subversion.CheckoutUpdater; +import hudson.scm.subversion.Messages; +import hudson.scm.subversion.SvnHelper; +import hudson.scm.subversion.UpdateUpdater; +import hudson.scm.subversion.UpdateWithRevertUpdater; +import hudson.scm.subversion.WorkspaceUpdater; +import hudson.scm.subversion.WorkspaceUpdater.UpdateTask; +import hudson.scm.subversion.WorkspaceUpdaterDescriptor; +import hudson.util.FormValidation; +import hudson.util.LogTaskListener; +import hudson.util.MultipartFormDataParser; +import hudson.util.Scrambler; +import hudson.util.Secret; +import hudson.util.TimeUnit2; + +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Random; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.UUID; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import javax.servlet.ServletException; +import javax.xml.transform.stream.StreamResult; + +import net.sf.json.JSONObject; + +import org.acegisecurity.context.SecurityContext; +import org.acegisecurity.context.SecurityContextHolder; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.taskdefs.Chmod; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; +import org.kohsuke.stapler.AncestorInPath; +import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.QueryParameter; +import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.stapler.StaplerResponse; +import org.kohsuke.stapler.export.Exported; +import org.kohsuke.stapler.export.ExportedBean; +import org.tmatesoft.svn.core.*; +import org.tmatesoft.svn.core.auth.*; +import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; +import org.tmatesoft.svn.core.internal.io.dav.http.DefaultHTTPConnectionFactory; +import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory; +import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; +import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; +import org.tmatesoft.svn.core.internal.wc.SVNPath; +import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory; +import org.tmatesoft.svn.core.io.SVNCapability; +import org.tmatesoft.svn.core.io.SVNRepository; +import org.tmatesoft.svn.core.io.SVNRepositoryFactory; +import org.tmatesoft.svn.core.io.ISVNSession; +import org.tmatesoft.svn.core.wc.SVNClientManager; +import org.tmatesoft.svn.core.wc.SVNInfo; +import org.tmatesoft.svn.core.wc.SVNRevision; +import org.tmatesoft.svn.core.wc.SVNWCClient; +import org.tmatesoft.svn.core.wc.SVNWCUtil; + +import com.trilead.ssh2.DebugLogger; +import com.trilead.ssh2.SCPClient; +import com.trilead.ssh2.crypto.Base64; +import javax.annotation.Nonnull; +import jenkins.MasterToSlaveFileCallable; +import jenkins.security.Roles; +import jenkins.security.SlaveToMasterCallable; +import org.jenkinsci.remoting.RoleChecker; + + +public class AdditionalCredentials extends AbstractDescribableImpl { + @NonNull + private final String realm; + @CheckForNull + private final String credentialsId; + + @DataBoundConstructor + public AdditionalCredentials(@NonNull String realm, @CheckForNull String credentialsId) { + realm.getClass(); // throw NPE if null + this.realm = realm; + this.credentialsId = credentialsId; + } + + @NonNull + public String getRealm() { + return realm; + } + + @CheckForNull + public String getCredentialsId() { + return credentialsId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AdditionalCredentials)) { + return false; + } + + AdditionalCredentials that = (AdditionalCredentials) o; + + if (!realm.equals(that.realm)) { + return false; + } + if (credentialsId != null ? !credentialsId.equals(that.credentialsId) : that.credentialsId != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = realm.hashCode(); + result = 31 * result + (credentialsId != null ? credentialsId.hashCode() : 0); + return result; + } + + @Extension + public static class DescriptorImpl extends Descriptor { + + @Override + public String getDisplayName() { + return null; + } + + public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, + @QueryParameter String realm) { + if (context == null && !Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER) || + context != null && !context.hasPermission(Item.EXTENDED_READ)) { + return new StandardListBoxModel(); + } + List domainRequirements; + if (realm == null) { + domainRequirements = Collections.emptyList(); + } else { + if (realm.startsWith("<") && realm.contains(">")) { + int index = realm.indexOf('>'); + assert index > 1; + domainRequirements = URIRequirementBuilder.fromUri(realm.substring(1, index).trim()).build(); + } else { + domainRequirements = Collections.emptyList(); + } + } + return new StandardListBoxModel() + .withEmptySelection() + .withMatching( + CredentialsMatchers.anyOf( + CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class), + CredentialsMatchers.instanceOf(StandardCertificateCredentials.class), + CredentialsMatchers.instanceOf(SSHUserPrivateKey.class) + ), + CredentialsProvider.lookupCredentials(StandardCredentials.class, + context, + ACL.SYSTEM, + domainRequirements) + ); + } + + } +} \ No newline at end of file diff --git a/src/main/java/hudson/scm/CredentialsSVNAuthenticationProviderImpl.java b/src/main/java/hudson/scm/CredentialsSVNAuthenticationProviderImpl.java index 4c0cf4423..2ebfb87bd 100644 --- a/src/main/java/hudson/scm/CredentialsSVNAuthenticationProviderImpl.java +++ b/src/main/java/hudson/scm/CredentialsSVNAuthenticationProviderImpl.java @@ -132,7 +132,7 @@ public static CredentialsSVNAuthenticationProviderImpl createAuthenticationProvi } Map additional = new HashMap(); if (scm != null) { - for (SubversionSCM.AdditionalCredentials c : scm.getAdditionalCredentials()) { + for (AdditionalCredentials c : scm.getAdditionalCredentials()) { if (c.getCredentialsId() != null) { StandardCredentials cred = CredentialsMatchers .firstOrNull(CredentialsProvider.lookupCredentials(StandardCredentials.class, context, diff --git a/src/main/java/hudson/scm/SubversionSCM.java b/src/main/java/hudson/scm/SubversionSCM.java index 0b8254237..54f1b6d86 100755 --- a/src/main/java/hudson/scm/SubversionSCM.java +++ b/src/main/java/hudson/scm/SubversionSCM.java @@ -91,6 +91,7 @@ import hudson.scm.subversion.WorkspaceUpdater; import hudson.scm.subversion.WorkspaceUpdater.UpdateTask; import hudson.scm.subversion.WorkspaceUpdaterDescriptor; +import hudson.scm.AdditionalCredentials; import hudson.util.FormValidation; import hudson.util.LogTaskListener; import hudson.util.MultipartFormDataParser; @@ -297,7 +298,7 @@ public SubversionSCM(List locations, */ public SubversionSCM(List locations, boolean useUpdate, SubversionRepositoryBrowser browser, String excludedRegions, String excludedUsers, String excludedRevprop, String excludedCommitMessages) { - this(locations, useUpdate, false, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages); + this(locations, useUpdate, false, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages); } /** @@ -372,28 +373,35 @@ public SubversionSCM(List locations, WorkspaceUpdater workspaceU * Convenience constructor, especially during testing. */ public SubversionSCM(String svnUrl) { - this(svnUrl, null, "."); + this(svnUrl, null, ".", new ArrayList()); } /** * Convenience constructor, especially during testing. */ public SubversionSCM(String svnUrl, String local) { - this(svnUrl, null, local); + this(svnUrl, null, local, new ArrayList()); } /** * Convenience constructor, especially during testing. */ public SubversionSCM(String svnUrl, String credentialId, String local) { - this(new String[]{svnUrl}, new String[]{credentialId}, new String[]{local}); + this(new String[]{svnUrl}, new String[]{credentialId}, new String[]{local}, new ArrayList()); + } + + /** + * Convenience constructor, especially during testing. + */ + public SubversionSCM(String svnUrl, String credentialId, String local, List additionalCredentials) { + this(new String[]{svnUrl}, new String[]{credentialId}, new String[]{local}, additionalCredentials); } /** * Convenience constructor, especially during testing. */ public SubversionSCM(String[] svnUrls, String[] locals) { - this(svnUrls, null, locals); + this(svnUrls, null, locals, new ArrayList()); } /** @@ -403,6 +411,14 @@ public SubversionSCM(String[] svnUrls, String[] credentialIds, String[] locals) this(ModuleLocation.parse(svnUrls, credentialIds, locals, null,null), true, false, null, null, null, null, null); } + + /** + * Convenience constructor, especially during testing. + */ + public SubversionSCM(String[] svnUrls, String[] credentialIds, String[] locals, List additionalCredentials) { + this(ModuleLocation.parse(svnUrls, credentialIds, locals, null,null), new UpdateUpdater(), null, null, null, null, null, null, false, false, additionalCredentials); + } + /** * @deprecated * as of 1.91. Use {@link #getLocations()} instead. @@ -418,7 +434,7 @@ public String getModules() { */ @Exported public ModuleLocation[] getLocations() { - return getLocations(null, null); + return getLocations(null, null); } @Override public String getKey() { @@ -777,36 +793,36 @@ private void calcChangeLog(Run build, FilePath workspace, File changelogFil try { String line; while((line=br.readLine())!=null) { - boolean isPinned = false; - int indexLast = line.length(); - if (line.lastIndexOf("::p") == indexLast-3) { - isPinned = true; - indexLast -= 3; - } - int index = line.lastIndexOf('/'); + boolean isPinned = false; + int indexLast = line.length(); + if (line.lastIndexOf("::p") == indexLast-3) { + isPinned = true; + indexLast -= 3; + } + int index = line.lastIndexOf('/'); if(index<0) { continue; // invalid line? } try { - String url = line.substring(0, index); - long revision = Long.parseLong(line.substring(index+1,indexLast)); - Long oldRevision = revisions.get(url); - if (isPinned) { - if (!prunePinnedExternals) { - if (oldRevision == null) - // If we're writing pinned, only write if there are no unpinned - revisions.put(url, revision); - } - } else { - // unpinned - if (oldRevision == null || oldRevision > revision) - // For unpinned, take minimum - revisions.put(url, revision); - } - } catch (NumberFormatException e) { - // perhaps a corrupted line. - LOGGER.log(WARNING, "Error parsing line " + line, e); - } + String url = line.substring(0, index); + long revision = Long.parseLong(line.substring(index+1,indexLast)); + Long oldRevision = revisions.get(url); + if (isPinned) { + if (!prunePinnedExternals) { + if (oldRevision == null) + // If we're writing pinned, only write if there are no unpinned + revisions.put(url, revision); + } + } else { + // unpinned + if (oldRevision == null || oldRevision > revision) + // For unpinned, take minimum + revisions.put(url, revision); + } + } catch (NumberFormatException e) { + // perhaps a corrupted line. + LOGGER.log(WARNING, "Error parsing line " + line, e); + } } } finally { br.close(); @@ -2759,7 +2775,7 @@ public String getLocalDir() { * possible "@NNN" suffix. */ public String getURL() { - return SvnHelper.getUrlWithoutRevision(remote); + return SvnHelper.getUrlWithoutRevision(remote); } /** @@ -3238,98 +3254,5 @@ private static StandardCredentials lookupCredentials(Item context, String creden CredentialsMatchers.withId(credentialsId)); } - public static class AdditionalCredentials extends AbstractDescribableImpl { - @NonNull - private final String realm; - @CheckForNull - private final String credentialsId; - - @DataBoundConstructor - public AdditionalCredentials(@NonNull String realm, @CheckForNull String credentialsId) { - realm.getClass(); // throw NPE if null - this.realm = realm; - this.credentialsId = credentialsId; - } - - @NonNull - public String getRealm() { - return realm; - } - - @CheckForNull - public String getCredentialsId() { - return credentialsId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof AdditionalCredentials)) { - return false; - } - - AdditionalCredentials that = (AdditionalCredentials) o; - - if (!realm.equals(that.realm)) { - return false; - } - if (credentialsId != null ? !credentialsId.equals(that.credentialsId) : that.credentialsId != null) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = realm.hashCode(); - result = 31 * result + (credentialsId != null ? credentialsId.hashCode() : 0); - return result; - } - - @Extension - public static class DescriptorImpl extends Descriptor { - - @Override - public String getDisplayName() { - return null; - } - - public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, - @QueryParameter String realm) { - if (context == null && !Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER) || - context != null && !context.hasPermission(Item.EXTENDED_READ)) { - return new StandardListBoxModel(); - } - List domainRequirements; - if (realm == null) { - domainRequirements = Collections.emptyList(); - } else { - if (realm.startsWith("<") && realm.contains(">")) { - int index = realm.indexOf('>'); - assert index > 1; - domainRequirements = URIRequirementBuilder.fromUri(realm.substring(1, index).trim()).build(); - } else { - domainRequirements = Collections.emptyList(); - } - } - return new StandardListBoxModel() - .withEmptySelection() - .withMatching( - CredentialsMatchers.anyOf( - CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class), - CredentialsMatchers.instanceOf(StandardCertificateCredentials.class), - CredentialsMatchers.instanceOf(SSHUserPrivateKey.class) - ), - CredentialsProvider.lookupCredentials(StandardCredentials.class, - context, - ACL.SYSTEM, - domainRequirements) - ); - } - - } - } + } diff --git a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java index d460ae1e2..43abed8c0 100644 --- a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java +++ b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java @@ -45,6 +45,7 @@ import hudson.scm.FilterSVNAuthenticationManager; import hudson.scm.SubversionRepositoryStatus; import hudson.scm.SubversionSCM; +import hudson.scm.AdditionalCredentials; import hudson.scm.subversion.SvnHelper; import hudson.security.ACL; import hudson.util.EditDistance; @@ -121,6 +122,8 @@ public class SubversionSCMSource extends SCMSource { private final String remoteBase; private String credentialsId = ""; // TODO null would be a better default, but need to check null safety on usages + + private List additionalCredentials; private String includes = DescriptorImpl.DEFAULT_INCLUDES; @@ -136,13 +139,23 @@ public SubversionSCMSource(String id, String remoteBase, String credentialsId, S setCredentialsId(credentialsId); setIncludes(StringUtils.defaultIfEmpty(includes, DescriptorImpl.DEFAULT_INCLUDES)); setExcludes(StringUtils.defaultIfEmpty(excludes, DescriptorImpl.DEFAULT_EXCLUDES)); + this.additionalCredentials = new ArrayList(); + } + + //kept old constructor without additionalCredentials for backwards compatibility + @SuppressWarnings("unused") // by stapler + public SubversionSCMSource(String id, String remoteBase) { + super(id); + this.remoteBase = StringUtils.removeEnd(remoteBase, "/") + "/"; + this.additionalCredentials = new ArrayList(); } @DataBoundConstructor @SuppressWarnings("unused") // by stapler - public SubversionSCMSource(String id, String remoteBase) { + public SubversionSCMSource(String id, String remoteBase, List additionalCredentials) { super(id); this.remoteBase = StringUtils.removeEnd(remoteBase, "/") + "/"; + this.additionalCredentials = additionalCredentials; } /** @@ -159,6 +172,14 @@ public String getCredentialsId() { public void setCredentialsId(String credentialsId) { this.credentialsId = credentialsId; } + @DataBoundSetter + public void setAdditionalCredentials(List additionalCredentials) { + this.additionalCredentials = additionalCredentials; + } + //without getter jelly will crash + public List getAdditionalCredentials() { + return(additionalCredentials); + } /** * Gets the comma separated list of exclusions. @@ -273,7 +294,7 @@ protected SCMRevision retrieve(@NonNull SCMHead head, @NonNull TaskListener list String repoPath = SubversionSCM.DescriptorImpl.getRelativePath(repoURL, repository.getRepository()); String path = SVNPathUtil.append(repoPath, head.getName()); SVNRepositoryView.NodeEntry svnEntry = repository.getNode(path, -1); - return new SCMRevisionImpl(head, svnEntry.getRevision()); + return new SCMRevisionImpl(head, svnEntry.getRevision(), (additionalCredentials.size()>0)); } catch (SVNException e) { throw new IOException(e); } finally { @@ -308,7 +329,7 @@ protected SCMRevision retrieve(String unparsedRevision, TaskListener listener) t listener.getLogger().println("Could not find " + path); return null; } - return new SCMRevisionImpl(new SCMHead(base), revision == -1 ? resolvedRevision : revision); + return new SCMRevisionImpl(new SCMHead(base), revision == -1 ? resolvedRevision : revision, (additionalCredentials.size()>0)); } catch (SVNException e) { throw new IOException(e); } finally { @@ -409,7 +430,7 @@ public boolean exists(@NonNull String path) throws IOException { }, listener)) { listener.getLogger().println("Met criteria"); SCMHead head = new SCMHead(childPath); - observer.observe(head, new SCMRevisionImpl(head, svnEntry.getRevision())); + observer.observe(head, new SCMRevisionImpl(head, svnEntry.getRevision(), (additionalCredentials.size()>0))); if (!observer.isObserving()) { return; } @@ -689,9 +710,10 @@ public SubversionSCM build(@NonNull SCMHead head, // name contains an @ so need to ensure there is an @ at the end of the name remote.append('@'); } - return new SubversionSCM(remote.toString(), credentialsId, "."); + return new SubversionSCM(remote.toString(), credentialsId, ".", additionalCredentials); } + /** * Our implementation. */ @@ -701,16 +723,29 @@ public static class SCMRevisionImpl extends SCMRevision { * The subversion revision. */ private long revision; + /** + * Indicates whether externals are used. If so, revision is considered non-deterministic. + */ + private boolean usesExternals; - public SCMRevisionImpl(SCMHead head, long revision) { + public SCMRevisionImpl(SCMHead head, long revision, boolean usesExternals) { super(head); this.revision = revision; + this.usesExternals = usesExternals; } public long getRevision() { return revision; } + public boolean isDeterministic() { + //non-deterministic when using externals, to force Jenkins to recurse into subdirectories + if(usesExternals) { + return false; + } + return true; + } + /** * {@inheritDoc} */ diff --git a/src/main/resources/jenkins/scm/impl/subversion/SubversionSCMSource/config-detail.jelly b/src/main/resources/jenkins/scm/impl/subversion/SubversionSCMSource/config-detail.jelly index d3938aa53..18df072c0 100644 --- a/src/main/resources/jenkins/scm/impl/subversion/SubversionSCMSource/config-detail.jelly +++ b/src/main/resources/jenkins/scm/impl/subversion/SubversionSCMSource/config-detail.jelly @@ -36,4 +36,7 @@ + + + \ No newline at end of file diff --git a/src/test/java/hudson/scm/CredentialsExternalsTest.java b/src/test/java/hudson/scm/CredentialsExternalsTest.java index 93104e35f..7899d6842 100644 --- a/src/test/java/hudson/scm/CredentialsExternalsTest.java +++ b/src/test/java/hudson/scm/CredentialsExternalsTest.java @@ -116,7 +116,7 @@ public void smokes() throws Exception { p.setScm(new SubversionSCM( Collections.singletonList(new SubversionSCM.ModuleLocation("svn://localhost:" + mainPort + "/prj/trunk", "main-creds", ".", "", false)), null, null, null, null, null, null, null, false, false, // WTF was all that? - Collections.singletonList(new SubversionSCM.AdditionalCredentials(" " + ext.uuid(), "ext-creds")))); + Collections.singletonList(new AdditionalCredentials(" " + ext.uuid(), "ext-creds")))); FreeStyleBuild b = r.buildAndAssertSuccess(p); assertEquals("", b.getWorkspace().child("file").readToString()); assertEquals("", b.getWorkspace().child("ext/file").readToString()); From 11db817e75c4654413f47d4a50a6bfc7bc99cf3b Mon Sep 17 00:00:00 2001 From: Funke Date: Thu, 22 Jun 2017 11:26:36 +0200 Subject: [PATCH 2/5] moved AdditionalCredentials back to SubversionSCM --- .../hudson/scm/AdditionalCredentials.java | 252 ------------------ ...dentialsSVNAuthenticationProviderImpl.java | 2 +- src/main/java/hudson/scm/SubversionSCM.java | 96 ++++++- .../impl/subversion/SubversionSCMSource.java | 11 +- .../hudson/scm/CredentialsExternalsTest.java | 2 +- 5 files changed, 98 insertions(+), 265 deletions(-) delete mode 100644 src/main/java/hudson/scm/AdditionalCredentials.java diff --git a/src/main/java/hudson/scm/AdditionalCredentials.java b/src/main/java/hudson/scm/AdditionalCredentials.java deleted file mode 100644 index fd179b0f5..000000000 --- a/src/main/java/hudson/scm/AdditionalCredentials.java +++ /dev/null @@ -1,252 +0,0 @@ -package hudson.scm; - -import static hudson.Util.fixEmptyAndTrim; -import static hudson.scm.PollingResult.BUILD_NOW; -import static hudson.scm.PollingResult.NO_CHANGES; -import static java.util.logging.Level.FINE; -import static java.util.logging.Level.WARNING; - -import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; -import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey; -import com.cloudbees.plugins.credentials.Credentials; -import com.cloudbees.plugins.credentials.CredentialsMatchers; -import com.cloudbees.plugins.credentials.CredentialsNameProvider; -import com.cloudbees.plugins.credentials.CredentialsProvider; -import com.cloudbees.plugins.credentials.CredentialsScope; -import com.cloudbees.plugins.credentials.CredentialsStore; -import com.cloudbees.plugins.credentials.common.StandardCertificateCredentials; -import com.cloudbees.plugins.credentials.common.StandardCredentials; -import com.cloudbees.plugins.credentials.common.StandardListBoxModel; -import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials; -import com.cloudbees.plugins.credentials.domains.Domain; -import com.cloudbees.plugins.credentials.domains.DomainRequirement; -import com.cloudbees.plugins.credentials.domains.DomainSpecification; -import com.cloudbees.plugins.credentials.domains.HostnameRequirement; -import com.cloudbees.plugins.credentials.domains.HostnameSpecification; -import com.cloudbees.plugins.credentials.domains.SchemeRequirement; -import com.cloudbees.plugins.credentials.domains.SchemeSpecification; -import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder; -import com.cloudbees.plugins.credentials.impl.CertificateCredentialsImpl; -import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; -import edu.umd.cs.findbugs.annotations.CheckForNull; -import edu.umd.cs.findbugs.annotations.NonNull; -import hudson.BulkChange; -import hudson.EnvVars; -import hudson.Extension; -import hudson.FilePath; -import hudson.FilePath.FileCallable; -import hudson.Launcher; -import hudson.Util; -import hudson.init.InitMilestone; -import hudson.model.*; - -import java.io.ByteArrayOutputStream; -import java.nio.charset.UnsupportedCharsetException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.WeakHashMap; - -import hudson.security.ACL; -import hudson.util.ListBoxModel; -import jenkins.model.Jenkins; -import hudson.remoting.Channel; -import hudson.remoting.VirtualChannel; -import hudson.scm.UserProvidedCredential.AuthenticationManagerImpl; -import hudson.scm.subversion.CheckoutUpdater; -import hudson.scm.subversion.Messages; -import hudson.scm.subversion.SvnHelper; -import hudson.scm.subversion.UpdateUpdater; -import hudson.scm.subversion.UpdateWithRevertUpdater; -import hudson.scm.subversion.WorkspaceUpdater; -import hudson.scm.subversion.WorkspaceUpdater.UpdateTask; -import hudson.scm.subversion.WorkspaceUpdaterDescriptor; -import hudson.util.FormValidation; -import hudson.util.LogTaskListener; -import hudson.util.MultipartFormDataParser; -import hudson.util.Scrambler; -import hudson.util.Secret; -import hudson.util.TimeUnit2; - -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.OutputStream; -import java.io.PrintStream; -import java.io.PrintWriter; -import java.io.Serializable; -import java.io.StringWriter; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Random; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.UUID; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -import javax.servlet.ServletException; -import javax.xml.transform.stream.StreamResult; - -import net.sf.json.JSONObject; - -import org.acegisecurity.context.SecurityContext; -import org.acegisecurity.context.SecurityContextHolder; -import org.apache.commons.beanutils.PropertyUtils; -import org.apache.commons.io.FileUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.tools.ant.BuildException; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.taskdefs.Chmod; -import org.kohsuke.accmod.Restricted; -import org.kohsuke.accmod.restrictions.NoExternalUse; -import org.kohsuke.stapler.AncestorInPath; -import org.kohsuke.stapler.DataBoundConstructor; -import org.kohsuke.stapler.QueryParameter; -import org.kohsuke.stapler.StaplerRequest; -import org.kohsuke.stapler.StaplerResponse; -import org.kohsuke.stapler.export.Exported; -import org.kohsuke.stapler.export.ExportedBean; -import org.tmatesoft.svn.core.*; -import org.tmatesoft.svn.core.auth.*; -import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory; -import org.tmatesoft.svn.core.internal.io.dav.http.DefaultHTTPConnectionFactory; -import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory; -import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl; -import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions; -import org.tmatesoft.svn.core.internal.wc.SVNPath; -import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaFactory; -import org.tmatesoft.svn.core.io.SVNCapability; -import org.tmatesoft.svn.core.io.SVNRepository; -import org.tmatesoft.svn.core.io.SVNRepositoryFactory; -import org.tmatesoft.svn.core.io.ISVNSession; -import org.tmatesoft.svn.core.wc.SVNClientManager; -import org.tmatesoft.svn.core.wc.SVNInfo; -import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc.SVNWCClient; -import org.tmatesoft.svn.core.wc.SVNWCUtil; - -import com.trilead.ssh2.DebugLogger; -import com.trilead.ssh2.SCPClient; -import com.trilead.ssh2.crypto.Base64; -import javax.annotation.Nonnull; -import jenkins.MasterToSlaveFileCallable; -import jenkins.security.Roles; -import jenkins.security.SlaveToMasterCallable; -import org.jenkinsci.remoting.RoleChecker; - - -public class AdditionalCredentials extends AbstractDescribableImpl { - @NonNull - private final String realm; - @CheckForNull - private final String credentialsId; - - @DataBoundConstructor - public AdditionalCredentials(@NonNull String realm, @CheckForNull String credentialsId) { - realm.getClass(); // throw NPE if null - this.realm = realm; - this.credentialsId = credentialsId; - } - - @NonNull - public String getRealm() { - return realm; - } - - @CheckForNull - public String getCredentialsId() { - return credentialsId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof AdditionalCredentials)) { - return false; - } - - AdditionalCredentials that = (AdditionalCredentials) o; - - if (!realm.equals(that.realm)) { - return false; - } - if (credentialsId != null ? !credentialsId.equals(that.credentialsId) : that.credentialsId != null) { - return false; - } - - return true; - } - - @Override - public int hashCode() { - int result = realm.hashCode(); - result = 31 * result + (credentialsId != null ? credentialsId.hashCode() : 0); - return result; - } - - @Extension - public static class DescriptorImpl extends Descriptor { - - @Override - public String getDisplayName() { - return null; - } - - public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, - @QueryParameter String realm) { - if (context == null && !Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER) || - context != null && !context.hasPermission(Item.EXTENDED_READ)) { - return new StandardListBoxModel(); - } - List domainRequirements; - if (realm == null) { - domainRequirements = Collections.emptyList(); - } else { - if (realm.startsWith("<") && realm.contains(">")) { - int index = realm.indexOf('>'); - assert index > 1; - domainRequirements = URIRequirementBuilder.fromUri(realm.substring(1, index).trim()).build(); - } else { - domainRequirements = Collections.emptyList(); - } - } - return new StandardListBoxModel() - .withEmptySelection() - .withMatching( - CredentialsMatchers.anyOf( - CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class), - CredentialsMatchers.instanceOf(StandardCertificateCredentials.class), - CredentialsMatchers.instanceOf(SSHUserPrivateKey.class) - ), - CredentialsProvider.lookupCredentials(StandardCredentials.class, - context, - ACL.SYSTEM, - domainRequirements) - ); - } - - } -} \ No newline at end of file diff --git a/src/main/java/hudson/scm/CredentialsSVNAuthenticationProviderImpl.java b/src/main/java/hudson/scm/CredentialsSVNAuthenticationProviderImpl.java index 2ebfb87bd..4c0cf4423 100644 --- a/src/main/java/hudson/scm/CredentialsSVNAuthenticationProviderImpl.java +++ b/src/main/java/hudson/scm/CredentialsSVNAuthenticationProviderImpl.java @@ -132,7 +132,7 @@ public static CredentialsSVNAuthenticationProviderImpl createAuthenticationProvi } Map additional = new HashMap(); if (scm != null) { - for (AdditionalCredentials c : scm.getAdditionalCredentials()) { + for (SubversionSCM.AdditionalCredentials c : scm.getAdditionalCredentials()) { if (c.getCredentialsId() != null) { StandardCredentials cred = CredentialsMatchers .firstOrNull(CredentialsProvider.lookupCredentials(StandardCredentials.class, context, diff --git a/src/main/java/hudson/scm/SubversionSCM.java b/src/main/java/hudson/scm/SubversionSCM.java index 54f1b6d86..597267979 100755 --- a/src/main/java/hudson/scm/SubversionSCM.java +++ b/src/main/java/hudson/scm/SubversionSCM.java @@ -91,7 +91,6 @@ import hudson.scm.subversion.WorkspaceUpdater; import hudson.scm.subversion.WorkspaceUpdater.UpdateTask; import hudson.scm.subversion.WorkspaceUpdaterDescriptor; -import hudson.scm.AdditionalCredentials; import hudson.util.FormValidation; import hudson.util.LogTaskListener; import hudson.util.MultipartFormDataParser; @@ -3253,6 +3252,99 @@ private static StandardCredentials lookupCredentials(Item context, String creden URIRequirementBuilder.fromUri(repoURL.toString()).build()), CredentialsMatchers.withId(credentialsId)); } - + public static class AdditionalCredentials extends AbstractDescribableImpl { + @NonNull + private final String realm; + @CheckForNull + private final String credentialsId; + + @DataBoundConstructor + public AdditionalCredentials(@NonNull String realm, @CheckForNull String credentialsId) { + realm.getClass(); // throw NPE if null + this.realm = realm; + this.credentialsId = credentialsId; + } + + @NonNull + public String getRealm() { + return realm; + } + + @CheckForNull + public String getCredentialsId() { + return credentialsId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AdditionalCredentials)) { + return false; + } + + AdditionalCredentials that = (AdditionalCredentials) o; + + if (!realm.equals(that.realm)) { + return false; + } + if (credentialsId != null ? !credentialsId.equals(that.credentialsId) : that.credentialsId != null) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int result = realm.hashCode(); + result = 31 * result + (credentialsId != null ? credentialsId.hashCode() : 0); + return result; + } + + @Extension + public static class DescriptorImpl extends Descriptor { + + @Override + public String getDisplayName() { + return null; + } + + public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, + @QueryParameter String realm) { + if (context == null && !Jenkins.getActiveInstance().hasPermission(Jenkins.ADMINISTER) || + context != null && !context.hasPermission(Item.EXTENDED_READ)) { + return new StandardListBoxModel(); + } + List domainRequirements; + if (realm == null) { + domainRequirements = Collections.emptyList(); + } else { + if (realm.startsWith("<") && realm.contains(">")) { + int index = realm.indexOf('>'); + assert index > 1; + domainRequirements = URIRequirementBuilder.fromUri(realm.substring(1, index).trim()).build(); + } else { + domainRequirements = Collections.emptyList(); + } + } + return new StandardListBoxModel() + .withEmptySelection() + .withMatching( + CredentialsMatchers.anyOf( + CredentialsMatchers.instanceOf(StandardUsernamePasswordCredentials.class), + CredentialsMatchers.instanceOf(StandardCertificateCredentials.class), + CredentialsMatchers.instanceOf(SSHUserPrivateKey.class) + ), + CredentialsProvider.lookupCredentials(StandardCredentials.class, + context, + ACL.SYSTEM, + domainRequirements) + ); + } + + } + } } diff --git a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java index 43abed8c0..08fad3a82 100644 --- a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java +++ b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java @@ -45,7 +45,7 @@ import hudson.scm.FilterSVNAuthenticationManager; import hudson.scm.SubversionRepositoryStatus; import hudson.scm.SubversionSCM; -import hudson.scm.AdditionalCredentials; +import hudson.scm.SubversionSCM.AdditionalCredentials; import hudson.scm.subversion.SvnHelper; import hudson.security.ACL; import hudson.util.EditDistance; @@ -142,7 +142,7 @@ public SubversionSCMSource(String id, String remoteBase, String credentialsId, S this.additionalCredentials = new ArrayList(); } - //kept old constructor without additionalCredentials for backwards compatibility + @DataBoundConstructor @SuppressWarnings("unused") // by stapler public SubversionSCMSource(String id, String remoteBase) { super(id); @@ -150,13 +150,6 @@ public SubversionSCMSource(String id, String remoteBase) { this.additionalCredentials = new ArrayList(); } - @DataBoundConstructor - @SuppressWarnings("unused") // by stapler - public SubversionSCMSource(String id, String remoteBase, List additionalCredentials) { - super(id); - this.remoteBase = StringUtils.removeEnd(remoteBase, "/") + "/"; - this.additionalCredentials = additionalCredentials; - } /** * Gets the credentials id. diff --git a/src/test/java/hudson/scm/CredentialsExternalsTest.java b/src/test/java/hudson/scm/CredentialsExternalsTest.java index 7899d6842..93104e35f 100644 --- a/src/test/java/hudson/scm/CredentialsExternalsTest.java +++ b/src/test/java/hudson/scm/CredentialsExternalsTest.java @@ -116,7 +116,7 @@ public void smokes() throws Exception { p.setScm(new SubversionSCM( Collections.singletonList(new SubversionSCM.ModuleLocation("svn://localhost:" + mainPort + "/prj/trunk", "main-creds", ".", "", false)), null, null, null, null, null, null, null, false, false, // WTF was all that? - Collections.singletonList(new AdditionalCredentials(" " + ext.uuid(), "ext-creds")))); + Collections.singletonList(new SubversionSCM.AdditionalCredentials(" " + ext.uuid(), "ext-creds")))); FreeStyleBuild b = r.buildAndAssertSuccess(p); assertEquals("", b.getWorkspace().child("file").readToString()); assertEquals("", b.getWorkspace().child("ext/file").readToString()); From fa293500ba4383f32673887a0ce9d590e32efcfb Mon Sep 17 00:00:00 2001 From: Funke Date: Fri, 23 Jun 2017 17:24:02 +0200 Subject: [PATCH 3/5] undo changes in SubversionSCM and more efficient initializing in SubversionSCMSource --- src/main/java/hudson/scm/SubversionSCM.java | 85 ++++++++----------- .../impl/subversion/SubversionSCMSource.java | 14 +-- 2 files changed, 43 insertions(+), 56 deletions(-) diff --git a/src/main/java/hudson/scm/SubversionSCM.java b/src/main/java/hudson/scm/SubversionSCM.java index 597267979..0b8254237 100755 --- a/src/main/java/hudson/scm/SubversionSCM.java +++ b/src/main/java/hudson/scm/SubversionSCM.java @@ -297,7 +297,7 @@ public SubversionSCM(List locations, */ public SubversionSCM(List locations, boolean useUpdate, SubversionRepositoryBrowser browser, String excludedRegions, String excludedUsers, String excludedRevprop, String excludedCommitMessages) { - this(locations, useUpdate, false, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages); + this(locations, useUpdate, false, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages); } /** @@ -372,35 +372,28 @@ public SubversionSCM(List locations, WorkspaceUpdater workspaceU * Convenience constructor, especially during testing. */ public SubversionSCM(String svnUrl) { - this(svnUrl, null, ".", new ArrayList()); + this(svnUrl, null, "."); } /** * Convenience constructor, especially during testing. */ public SubversionSCM(String svnUrl, String local) { - this(svnUrl, null, local, new ArrayList()); + this(svnUrl, null, local); } /** * Convenience constructor, especially during testing. */ public SubversionSCM(String svnUrl, String credentialId, String local) { - this(new String[]{svnUrl}, new String[]{credentialId}, new String[]{local}, new ArrayList()); - } - - /** - * Convenience constructor, especially during testing. - */ - public SubversionSCM(String svnUrl, String credentialId, String local, List additionalCredentials) { - this(new String[]{svnUrl}, new String[]{credentialId}, new String[]{local}, additionalCredentials); + this(new String[]{svnUrl}, new String[]{credentialId}, new String[]{local}); } /** * Convenience constructor, especially during testing. */ public SubversionSCM(String[] svnUrls, String[] locals) { - this(svnUrls, null, locals, new ArrayList()); + this(svnUrls, null, locals); } /** @@ -410,14 +403,6 @@ public SubversionSCM(String[] svnUrls, String[] credentialIds, String[] locals) this(ModuleLocation.parse(svnUrls, credentialIds, locals, null,null), true, false, null, null, null, null, null); } - - /** - * Convenience constructor, especially during testing. - */ - public SubversionSCM(String[] svnUrls, String[] credentialIds, String[] locals, List additionalCredentials) { - this(ModuleLocation.parse(svnUrls, credentialIds, locals, null,null), new UpdateUpdater(), null, null, null, null, null, null, false, false, additionalCredentials); - } - /** * @deprecated * as of 1.91. Use {@link #getLocations()} instead. @@ -433,7 +418,7 @@ public String getModules() { */ @Exported public ModuleLocation[] getLocations() { - return getLocations(null, null); + return getLocations(null, null); } @Override public String getKey() { @@ -792,36 +777,36 @@ private void calcChangeLog(Run build, FilePath workspace, File changelogFil try { String line; while((line=br.readLine())!=null) { - boolean isPinned = false; - int indexLast = line.length(); - if (line.lastIndexOf("::p") == indexLast-3) { - isPinned = true; - indexLast -= 3; - } - int index = line.lastIndexOf('/'); + boolean isPinned = false; + int indexLast = line.length(); + if (line.lastIndexOf("::p") == indexLast-3) { + isPinned = true; + indexLast -= 3; + } + int index = line.lastIndexOf('/'); if(index<0) { continue; // invalid line? } try { - String url = line.substring(0, index); - long revision = Long.parseLong(line.substring(index+1,indexLast)); - Long oldRevision = revisions.get(url); - if (isPinned) { - if (!prunePinnedExternals) { - if (oldRevision == null) - // If we're writing pinned, only write if there are no unpinned - revisions.put(url, revision); - } - } else { - // unpinned - if (oldRevision == null || oldRevision > revision) - // For unpinned, take minimum - revisions.put(url, revision); - } - } catch (NumberFormatException e) { - // perhaps a corrupted line. - LOGGER.log(WARNING, "Error parsing line " + line, e); - } + String url = line.substring(0, index); + long revision = Long.parseLong(line.substring(index+1,indexLast)); + Long oldRevision = revisions.get(url); + if (isPinned) { + if (!prunePinnedExternals) { + if (oldRevision == null) + // If we're writing pinned, only write if there are no unpinned + revisions.put(url, revision); + } + } else { + // unpinned + if (oldRevision == null || oldRevision > revision) + // For unpinned, take minimum + revisions.put(url, revision); + } + } catch (NumberFormatException e) { + // perhaps a corrupted line. + LOGGER.log(WARNING, "Error parsing line " + line, e); + } } } finally { br.close(); @@ -2774,7 +2759,7 @@ public String getLocalDir() { * possible "@NNN" suffix. */ public String getURL() { - return SvnHelper.getUrlWithoutRevision(remote); + return SvnHelper.getUrlWithoutRevision(remote); } /** @@ -3252,7 +3237,7 @@ private static StandardCredentials lookupCredentials(Item context, String creden URIRequirementBuilder.fromUri(repoURL.toString()).build()), CredentialsMatchers.withId(credentialsId)); } - + public static class AdditionalCredentials extends AbstractDescribableImpl { @NonNull private final String realm; @@ -3346,5 +3331,5 @@ public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Item context, } } - } + } } diff --git a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java index 08fad3a82..f3e613627 100644 --- a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java +++ b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java @@ -47,6 +47,7 @@ import hudson.scm.SubversionSCM; import hudson.scm.SubversionSCM.AdditionalCredentials; import hudson.scm.subversion.SvnHelper; +import hudson.scm.subversion.UpdateUpdater; import hudson.security.ACL; import hudson.util.EditDistance; import hudson.util.FormValidation; @@ -139,7 +140,6 @@ public SubversionSCMSource(String id, String remoteBase, String credentialsId, S setCredentialsId(credentialsId); setIncludes(StringUtils.defaultIfEmpty(includes, DescriptorImpl.DEFAULT_INCLUDES)); setExcludes(StringUtils.defaultIfEmpty(excludes, DescriptorImpl.DEFAULT_EXCLUDES)); - this.additionalCredentials = new ArrayList(); } @DataBoundConstructor @@ -147,7 +147,6 @@ public SubversionSCMSource(String id, String remoteBase, String credentialsId, S public SubversionSCMSource(String id, String remoteBase) { super(id); this.remoteBase = StringUtils.removeEnd(remoteBase, "/") + "/"; - this.additionalCredentials = new ArrayList(); } @@ -171,6 +170,9 @@ public void setAdditionalCredentials(List additionalCrede } //without getter jelly will crash public List getAdditionalCredentials() { + if(additionalCredentials==null) { + return Collections.emptyList(); + } return(additionalCredentials); } @@ -287,7 +289,7 @@ protected SCMRevision retrieve(@NonNull SCMHead head, @NonNull TaskListener list String repoPath = SubversionSCM.DescriptorImpl.getRelativePath(repoURL, repository.getRepository()); String path = SVNPathUtil.append(repoPath, head.getName()); SVNRepositoryView.NodeEntry svnEntry = repository.getNode(path, -1); - return new SCMRevisionImpl(head, svnEntry.getRevision(), (additionalCredentials.size()>0)); + return new SCMRevisionImpl(head, svnEntry.getRevision(), (additionalCredentials!=null && additionalCredentials.size()>0)); } catch (SVNException e) { throw new IOException(e); } finally { @@ -322,7 +324,7 @@ protected SCMRevision retrieve(String unparsedRevision, TaskListener listener) t listener.getLogger().println("Could not find " + path); return null; } - return new SCMRevisionImpl(new SCMHead(base), revision == -1 ? resolvedRevision : revision, (additionalCredentials.size()>0)); + return new SCMRevisionImpl(new SCMHead(base), revision == -1 ? resolvedRevision : revision, (additionalCredentials!=null && additionalCredentials.size()>0)); } catch (SVNException e) { throw new IOException(e); } finally { @@ -423,7 +425,7 @@ public boolean exists(@NonNull String path) throws IOException { }, listener)) { listener.getLogger().println("Met criteria"); SCMHead head = new SCMHead(childPath); - observer.observe(head, new SCMRevisionImpl(head, svnEntry.getRevision(), (additionalCredentials.size()>0))); + observer.observe(head, new SCMRevisionImpl(head, svnEntry.getRevision(), (additionalCredentials!=null && additionalCredentials.size()>0))); if (!observer.isObserving()) { return; } @@ -703,7 +705,7 @@ public SubversionSCM build(@NonNull SCMHead head, // name contains an @ so need to ensure there is an @ at the end of the name remote.append('@'); } - return new SubversionSCM(remote.toString(), credentialsId, ".", additionalCredentials); + return new SubversionSCM(SubversionSCM.ModuleLocation.parse(new String[]{remote.toString()}, new String[]{credentialsId}, new String[]{"."}, null,null), new UpdateUpdater(), null, null, null, null, null, null, false, false, additionalCredentials); } From 3540bb6dedc2c8d11a381a595f4ba4390e2c4586 Mon Sep 17 00:00:00 2001 From: Funke Date: Fri, 7 Jul 2017 14:04:04 +0200 Subject: [PATCH 4/5] made getter for additional credentials restricted and undid changes regarding non-deterministic revision --- .../impl/subversion/SubversionSCMSource.java | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java index f3e613627..4db39e946 100644 --- a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java +++ b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java @@ -70,6 +70,8 @@ import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; +import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.NoExternalUse; import org.tmatesoft.svn.core.SVNDirEntry; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; @@ -141,7 +143,7 @@ public SubversionSCMSource(String id, String remoteBase, String credentialsId, S setIncludes(StringUtils.defaultIfEmpty(includes, DescriptorImpl.DEFAULT_INCLUDES)); setExcludes(StringUtils.defaultIfEmpty(excludes, DescriptorImpl.DEFAULT_EXCLUDES)); } - + @DataBoundConstructor @SuppressWarnings("unused") // by stapler public SubversionSCMSource(String id, String remoteBase) { @@ -149,7 +151,6 @@ public SubversionSCMSource(String id, String remoteBase) { this.remoteBase = StringUtils.removeEnd(remoteBase, "/") + "/"; } - /** * Gets the credentials id. * @@ -169,6 +170,7 @@ public void setAdditionalCredentials(List additionalCrede this.additionalCredentials = additionalCredentials; } //without getter jelly will crash + @Restricted(NoExternalUse.class) public List getAdditionalCredentials() { if(additionalCredentials==null) { return Collections.emptyList(); @@ -289,7 +291,7 @@ protected SCMRevision retrieve(@NonNull SCMHead head, @NonNull TaskListener list String repoPath = SubversionSCM.DescriptorImpl.getRelativePath(repoURL, repository.getRepository()); String path = SVNPathUtil.append(repoPath, head.getName()); SVNRepositoryView.NodeEntry svnEntry = repository.getNode(path, -1); - return new SCMRevisionImpl(head, svnEntry.getRevision(), (additionalCredentials!=null && additionalCredentials.size()>0)); + return new SCMRevisionImpl(head, svnEntry.getRevision()); } catch (SVNException e) { throw new IOException(e); } finally { @@ -324,7 +326,7 @@ protected SCMRevision retrieve(String unparsedRevision, TaskListener listener) t listener.getLogger().println("Could not find " + path); return null; } - return new SCMRevisionImpl(new SCMHead(base), revision == -1 ? resolvedRevision : revision, (additionalCredentials!=null && additionalCredentials.size()>0)); + return new SCMRevisionImpl(new SCMHead(base), revision == -1 ? resolvedRevision : revision); } catch (SVNException e) { throw new IOException(e); } finally { @@ -425,7 +427,7 @@ public boolean exists(@NonNull String path) throws IOException { }, listener)) { listener.getLogger().println("Met criteria"); SCMHead head = new SCMHead(childPath); - observer.observe(head, new SCMRevisionImpl(head, svnEntry.getRevision(), (additionalCredentials!=null && additionalCredentials.size()>0))); + observer.observe(head, new SCMRevisionImpl(head, svnEntry.getRevision())); if (!observer.isObserving()) { return; } @@ -708,7 +710,6 @@ public SubversionSCM build(@NonNull SCMHead head, return new SubversionSCM(SubversionSCM.ModuleLocation.parse(new String[]{remote.toString()}, new String[]{credentialsId}, new String[]{"."}, null,null), new UpdateUpdater(), null, null, null, null, null, null, false, false, additionalCredentials); } - /** * Our implementation. */ @@ -718,29 +719,16 @@ public static class SCMRevisionImpl extends SCMRevision { * The subversion revision. */ private long revision; - /** - * Indicates whether externals are used. If so, revision is considered non-deterministic. - */ - private boolean usesExternals; - public SCMRevisionImpl(SCMHead head, long revision, boolean usesExternals) { + public SCMRevisionImpl(SCMHead head, long revision) { super(head); this.revision = revision; - this.usesExternals = usesExternals; } public long getRevision() { return revision; } - public boolean isDeterministic() { - //non-deterministic when using externals, to force Jenkins to recurse into subdirectories - if(usesExternals) { - return false; - } - return true; - } - /** * {@inheritDoc} */ From 4a9730bd5d703b43ecd9ee8c2eded691956e3865 Mon Sep 17 00:00:00 2001 From: Funke Date: Mon, 10 Jul 2017 14:31:17 +0200 Subject: [PATCH 5/5] returns unmodifiableList in getter for additionalCredentials --- .../java/jenkins/scm/impl/subversion/SubversionSCMSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java index 4db39e946..f14b1fc72 100644 --- a/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java +++ b/src/main/java/jenkins/scm/impl/subversion/SubversionSCMSource.java @@ -175,7 +175,7 @@ public List getAdditionalCredentials() { if(additionalCredentials==null) { return Collections.emptyList(); } - return(additionalCredentials); + return Collections.unmodifiableList(additionalCredentials); } /**