Skip to content
Permalink
Browse files
[FIXED JENKINS-23511] Need to have a serialVersionUid on the class se…
…nt over remoting

- Also fixes the use of the wrong Callable in JENKINS-34336 (#18)
- Also fixes the whole pulling in of a Jenkins class when invoked from a slave
  • Loading branch information
stephenc committed Jun 9, 2016
1 parent 9724039 commit c50807ef8b4b3abdc41eaef861e28dc26f3843c2
Showing with 89 additions and 33 deletions.
  1. +89 −33 src/main/java/com/cloudbees/jenkins/plugins/sshcredentials/SSHAuthenticator.java
@@ -30,23 +30,22 @@
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.ExtensionList;
import hudson.model.BuildListener;
import hudson.model.Hudson;
import hudson.model.TaskListener;
import hudson.remoting.Channel;
import hudson.util.StreamTaskListener;
import jenkins.model.Jenkins;
import jenkins.security.MasterToSlaveCallable;
import net.jcip.annotations.GuardedBy;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import jenkins.model.Jenkins;
import jenkins.security.SlaveToMasterCallable;
import net.jcip.annotations.GuardedBy;

import static com.cloudbees.plugins.credentials.CredentialsMatchers.anyOf;
import static com.cloudbees.plugins.credentials.CredentialsMatchers.instanceOf;
@@ -100,7 +99,8 @@
*
* @param connection the connection we will be authenticating.
* @param user the user we will be authenticating as.
* @deprecated use {@link #SSHAuthenticator(Object, com.cloudbees.plugins.credentials.common.StandardUsernameCredentials, String)}
* @deprecated use
* {@link #SSHAuthenticator(Object, com.cloudbees.plugins.credentials.common.StandardUsernameCredentials, String)}
*/
@Deprecated
protected SSHAuthenticator(@NonNull C connection, @NonNull U user) {
@@ -183,7 +183,6 @@ public static <C, U extends StandardUsernameCredentials> SSHAuthenticator<C, U>
* @return a {@link SSHAuthenticator} that may or may not be able to successfully authenticate.
* @since 1.4
*/
@SuppressFBWarnings(value="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification="https://github.com/jenkinsci/jenkins/pull/2094")
@NonNull
public static <C, U extends StandardUsernameCredentials> SSHAuthenticator<C, U> newInstance(@NonNull C connection,
@NonNull U user,
@@ -193,18 +192,26 @@ public static <C, U extends StandardUsernameCredentials> SSHAuthenticator<C, U>
connection.getClass(); // throw NPE if null
user.getClass(); // throw NPE if null
Collection<SSHAuthenticatorFactory> factories;
if (Jenkins.getInstance() != null) {
// if running on the master
factories = Jenkins.getInstance().getExtensionList(SSHAuthenticatorFactory.class);
} else {
// if running on the slave, bring these factories over here
factories = Channel.current().call(new MasterToSlaveCallable<Collection<SSHAuthenticatorFactory>, IOException>() {
@SuppressFBWarnings(value="NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification="https://github.com/jenkinsci/jenkins/pull/2094")
public Collection<SSHAuthenticatorFactory> call() throws IOException {
return new ArrayList<SSHAuthenticatorFactory>(
Jenkins.getInstance().getExtensionList(SSHAuthenticatorFactory.class));
}
});
try {
factories = lookupFactories();
} catch (LinkageError e) {
// we are probably running on a remote agent and master only classes are banned from remote class loading
factories = null;
} catch (IllegalStateException e) {
// Jenkins.getInstance() is throwing an IllegalStateException when invoked on a remote agent
factories = null;
}
if (factories == null) {
// we are probably running on a remote agent
Channel channel = Channel.current();
if (channel == null) {
// ok we are not running on a remote agent, we have no ability to authenticate
factories = Collections.<SSHAuthenticatorFactory>emptySet();
} else {
// call back to the master and get an instance
factories = channel.call(new NewInstance());
}

}

for (SSHAuthenticatorFactory factory : factories) {
@@ -213,12 +220,21 @@ public Collection<SSHAuthenticatorFactory> call() throws IOException {
return result;
}
}
return new SSHAuthenticator<C, U>(connection, user, username) {
@Override
protected boolean doAuthenticate() {
return false;
}
};
return new SSHNonauthenticator<C, U>(connection, user, username);
}

/**
* This method allows a build agent to invoke {@link #newInstance(Object, StandardUsernameCredentials, String)}
* after we start blocking build agents from loading master-only classes such as {@link Jenkins} and
* {@link ExtensionList} as the JVM will not attempt to load these classes until this method gets invoked.
*
* @return the {@link SSHAuthenticatorFactory} instances (or {@code null} if you invoke from a build agent)
* @throws LinkageError if you invoke from a build agent
* @throws IllegalStateException if you invoke from a build agent
*/
private static List<SSHAuthenticatorFactory> lookupFactories() {
// TODO once Jenkins core has a class that can be used to detect running on build agent use that to gate instead
return Jenkins.getInstance() == null ? null : ExtensionList.lookup(SSHAuthenticatorFactory.class);
}

/**
@@ -239,7 +255,7 @@ public static SSHAuthenticator<Object, StandardUsernameCredentials> newInstance(
* @param <C> the type of connection.
* @param <U> the type of user.
* @return {@code true} if and only if the supplied connection class and user class are supported by at least one
* factory.
* factory.
*/
public static <C, U extends StandardUsernameCredentials> boolean isSupported(@NonNull Class<C> connectionClass,
@NonNull Class<U> userClass) {
@@ -260,8 +276,8 @@ public static <C, U extends StandardUsernameCredentials> boolean isSupported(@No
* @param connectionClass the type of connection to filter for.
* @return a list of {@link SSHUser} credentials appropriate for use with the supplied type of connection.
* @deprecated Use
* {@link CredentialsMatchers#filter(List, CredentialsMatcher)}
* and {@link #matcher(Class)}
* {@link CredentialsMatchers#filter(List, CredentialsMatcher)}
* and {@link #matcher(Class)}
*/
public static List<? extends StandardUsernameCredentials> filter(Iterable<? extends Credentials> credentials,
Class<?> connectionClass) {
@@ -282,7 +298,7 @@ public static List<? extends StandardUsernameCredentials> filter(Iterable<? exte
* When you know the connection type you will be using, it is better to use {@link #matcher(Class)}.
*
* @return a {@link CredentialsMatcher} that matches the generic types of credential that are valid for use over
* SSH.
* SSH.
*/
public static CredentialsMatcher matcher() {
return anyOf(
@@ -341,7 +357,7 @@ public boolean matches(@NonNull Credentials item) {
* </p>
*
* @return {@code true} if the bound connection is in a state where authentication can be tried using the
* supplied credentials.
* supplied credentials.
*/
public boolean canAuthenticate() {
synchronized (lock) {
@@ -406,7 +422,7 @@ public Mode getAuthenticationMode() {

/**
* @deprecated as of 0.3
* Use {@link #authenticate(TaskListener)} and provide a listener to receive errors.
* Use {@link #authenticate(TaskListener)} and provide a listener to receive errors.
*/
public final boolean authenticate() {
synchronized (lock) {
@@ -427,8 +443,8 @@ public final boolean authenticate() {
* Authenticate the bound connection using the supplied credentials.
*
* @return For an {@link #getAuthenticationMode()} of {@link Mode#BEFORE_CONNECT} the return value is
* always {@code true} otherwise the return value is {@code true} if and only if authentication was
* successful.
* always {@code true} otherwise the return value is {@code true} if and only if authentication was
* successful.
*/
public final boolean authenticate(TaskListener listener) {
setListener(listener);
@@ -464,4 +480,44 @@ public static enum Mode {
*/
AFTER_CONNECT;
}

/**
* A callable invoked from the remote agents to retrieve the {@link SSHAuthenticatorFactory} instances.
*/
private static class NewInstance extends SlaveToMasterCallable<Collection<SSHAuthenticatorFactory>, IOException> {
/**
* Standardize serialization.
*/
private static final long serialVersionUID = 1L;

/**
* {@inheritDoc}
*/
public Collection<SSHAuthenticatorFactory> call() throws IOException {
return new ArrayList<SSHAuthenticatorFactory>(ExtensionList.lookup(SSHAuthenticatorFactory.class));
}
}

/**
* A dummy {@link SSHAuthenticator} that will never authenticate.
*
* @param <C> the connection type.
* @param <U> the credential type.
*/
private static class SSHNonauthenticator<C, U extends StandardUsernameCredentials> extends SSHAuthenticator<C, U> {
/**
* {@inheritDoc}
*/
public SSHNonauthenticator(C connection, U user, String username) {
super(connection, user, username);
}

/**
* {@inheritDoc}
*/
@Override
protected boolean doAuthenticate() {
return false;
}
}
}

0 comments on commit c50807e

Please sign in to comment.