diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java index 942d13c82ce..64d43307ffc 100644 --- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java +++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java @@ -13,6 +13,8 @@ */ package org.apache.hadoop.security.authentication.client; +import com.google.common.annotations.VisibleForTesting; +import java.lang.reflect.Constructor; import org.apache.commons.codec.binary.Base64; import org.apache.hadoop.security.authentication.server.HttpConstants; import org.apache.hadoop.security.authentication.util.AuthToken; @@ -177,41 +179,65 @@ public class KerberosAuthenticator implements Authenticator { */ @Override public void authenticate(URL url, AuthenticatedURL.Token token) - throws IOException, AuthenticationException { + throws IOException, AuthenticationException { if (!token.isSet()) { this.url = url; base64 = new Base64(0); - HttpURLConnection conn = token.openConnection(url, connConfigurator); - conn.setRequestMethod(AUTH_HTTP_METHOD); - conn.connect(); - - boolean needFallback = false; - if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { - LOG.debug("JDK performed authentication on our behalf."); - // If the JDK already did the SPNEGO back-and-forth for - // us, just pull out the token. - AuthenticatedURL.extractToken(conn, token); - if (isTokenKerberos(token)) { - return; + try { + HttpURLConnection conn = token.openConnection(url, connConfigurator); + conn.setRequestMethod(AUTH_HTTP_METHOD); + conn.connect(); + + boolean needFallback = false; + if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) { + LOG.debug("JDK performed authentication on our behalf."); + // If the JDK already did the SPNEGO back-and-forth for + // us, just pull out the token. + AuthenticatedURL.extractToken(conn, token); + if (isTokenKerberos(token)) { + return; + } + needFallback = true; } - needFallback = true; - } - if (!needFallback && isNegotiate(conn)) { - LOG.debug("Performing our own SPNEGO sequence."); - doSpnegoSequence(token); - } else { - LOG.debug("Using fallback authenticator sequence."); - Authenticator auth = getFallBackAuthenticator(); - // Make sure that the fall back authenticator have the same - // ConnectionConfigurator, since the method might be overridden. - // Otherwise the fall back authenticator might not have the information - // to make the connection (e.g., SSL certificates) - auth.setConnectionConfigurator(connConfigurator); - auth.authenticate(url, token); + if (!needFallback && isNegotiate(conn)) { + LOG.debug("Performing our own SPNEGO sequence."); + doSpnegoSequence(token); + } else { + LOG.debug("Using fallback authenticator sequence."); + Authenticator auth = getFallBackAuthenticator(); + // Make sure that the fall back authenticator have the same + // ConnectionConfigurator, since the method might be overridden. + // Otherwise the fall back authenticator might not have the + // information to make the connection (e.g., SSL certificates) + auth.setConnectionConfigurator(connConfigurator); + auth.authenticate(url, token); + } + } catch (IOException ex){ + throw wrapExceptionWithMessage(ex, + "Error while authenticating with endpoint: " + url); + } catch (AuthenticationException ex){ + throw wrapExceptionWithMessage(ex, + "Error while authenticating with endpoint: " + url); } } } + @VisibleForTesting + static T wrapExceptionWithMessage( + T exception, String msg) { + Class exceptionClass = exception.getClass(); + try { + Constructor ctor = exceptionClass + .getConstructor(String.class); + Throwable t = ctor.newInstance(msg); + return (T) (t.initCause(exception)); + } catch (Throwable e) { + LOG.debug("Unable to wrap exception of type {}, it has " + + "no (String) constructor.", exceptionClass, e); + return exception; + } + } + /** * If the specified URL does not support SPNEGO authentication, a fallback {@link Authenticator} will be used. *

diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/client/TestKerberosAuthenticator.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/client/TestKerberosAuthenticator.java index 7db53bae067..4aabb34fa56 100644 --- a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/client/TestKerberosAuthenticator.java +++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/client/TestKerberosAuthenticator.java @@ -20,6 +20,9 @@ import static org.apache.hadoop.security.authentication.server.KerberosAuthentic import static org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler.KEYTAB; import static org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler.NAME_RULES; +import java.io.IOException; +import java.nio.charset.CharacterCodingException; +import javax.security.sasl.AuthenticationException; import org.apache.hadoop.minikdc.KerberosSecurityTestcase; import org.apache.hadoop.security.authentication.KerberosTestUtils; import org.apache.hadoop.security.authentication.server.AuthenticationFilter; @@ -218,4 +221,30 @@ public class TestKerberosAuthenticator extends KerberosSecurityTestcase { }); } + @Test(timeout = 60000) + public void testWrapExceptionWithMessage() { + IOException ex; + ex = new IOException("Induced exception"); + ex = KerberosAuthenticator.wrapExceptionWithMessage(ex, "Error while " + + "authenticating with endpoint: localhost"); + Assert.assertEquals("Induced exception", ex.getCause().getMessage()); + Assert.assertEquals("Error while authenticating with endpoint: localhost", + ex.getMessage()); + + ex = new AuthenticationException("Auth exception"); + ex = KerberosAuthenticator.wrapExceptionWithMessage(ex, "Error while " + + "authenticating with endpoint: localhost"); + Assert.assertEquals("Auth exception", ex.getCause().getMessage()); + Assert.assertEquals("Error while authenticating with endpoint: localhost", + ex.getMessage()); + + // Test for Exception with no (String) constructor + // redirect the LOG to and check log message + ex = new CharacterCodingException(); + Exception ex2 = KerberosAuthenticator.wrapExceptionWithMessage(ex, + "Error while authenticating with endpoint: localhost"); + Assert.assertTrue(ex instanceof CharacterCodingException); + Assert.assertTrue(ex.equals(ex2)); + } + }