You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by rg...@apache.org on 2018/03/14 14:27:13 UTC

qpid-jms git commit: QPIDJMS-367 : Add support for XOAUTH2 Sasl Autentication

Repository: qpid-jms
Updated Branches:
  refs/heads/master 7e7cebe50 -> 3d2ec9c5f


QPIDJMS-367 : Add support for XOAUTH2 Sasl Autentication


Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/3d2ec9c5
Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/3d2ec9c5
Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/3d2ec9c5

Branch: refs/heads/master
Commit: 3d2ec9c5fc65d512418b21086ee6a59a00bb2df4
Parents: 7e7cebe
Author: rgodfrey <rg...@apache.org>
Authored: Wed Mar 14 15:11:38 2018 +0100
Committer: rgodfrey <rg...@apache.org>
Committed: Wed Mar 14 15:21:55 2018 +0100

----------------------------------------------------------------------
 .../provider/amqp/AmqpSaslAuthenticator.java    |  18 +--
 .../org/apache/qpid/jms/sasl/Mechanism.java     |  10 ++
 .../apache/qpid/jms/sasl/XOauth2Mechanism.java  |  96 +++++++++++++
 .../qpid/jms/sasl/XOauth2MechanismFactory.java  |  28 ++++
 .../services/org/apache/qpid/jms/sasl/XOAUTH2   |  17 +++
 .../jms/integration/SaslIntegrationTest.java    |  57 +++++---
 .../qpid/jms/sasl/XOauth2MechanismTest.java     | 134 +++++++++++++++++++
 .../qpid/jms/test/testpeer/TestAmqpPeer.java    |  20 +++
 qpid-jms-docs/Configuration.md                  |   2 +-
 9 files changed, 358 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
index 41dc259..d7c8d1d 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/AmqpSaslAuthenticator.java
@@ -16,15 +16,14 @@
  */
 package org.apache.qpid.jms.provider.amqp;
 
-import java.util.function.Function;
-
-import javax.jms.JMSSecurityException;
-import javax.jms.JMSSecurityRuntimeException;
-
 import org.apache.qpid.jms.sasl.Mechanism;
 import org.apache.qpid.proton.engine.Sasl;
 import org.apache.qpid.proton.engine.Transport;
 
+import java.util.function.Function;
+import javax.jms.JMSSecurityException;
+import javax.jms.JMSSecurityRuntimeException;
+
 /**
  * Manage the SASL authentication process
  */
@@ -121,11 +120,14 @@ public class AmqpSaslAuthenticator {
     //----- Internal support methods -----------------------------------------//
 
     private void handleSaslFail() {
+        StringBuilder message = new StringBuilder("Client failed to authenticate");
         if (mechanism != null) {
-            recordFailure("Client failed to authenticate using SASL: " + mechanism.getName(), null);
-        } else {
-            recordFailure("Client failed to authenticate", null);
+            message.append(" using SASL: ").append(mechanism.getName());
+            if (mechanism.getAdditionalFailureInformation() != null) {
+                message.append(" (").append(mechanism.getAdditionalFailureInformation()).append(")");
+            }
         }
+        recordFailure(message.toString(), null);
     }
 
     private void handleSaslCompletion(Sasl sasl) {

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java
index 53e71f2..7f8c47e 100644
--- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/Mechanism.java
@@ -155,4 +155,14 @@ public interface Mechanism extends Comparable<Mechanism> {
      * @return true if this Mechanism is enabled by default.
      */
     boolean isEnabledByDefault();
+
+    /**
+     * Allows a mechanism to report additional information on the reason for
+     * authentication failure (e.g. provided in a challenge from the server)
+     *
+     * @return information on the reason for failure, or null if no such information is available
+     */
+    default String getAdditionalFailureInformation() {
+        return null;
+    }
 }

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/XOauth2Mechanism.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/XOauth2Mechanism.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/XOauth2Mechanism.java
new file mode 100644
index 0000000..ad2e424
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/XOauth2Mechanism.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.jms.sasl;
+
+import java.nio.charset.StandardCharsets;
+import java.security.Principal;
+import java.util.Base64;
+
+/**
+ * Implements the SASL XOAUTH2 authentication Mechanism .
+ *
+ * User name and Password values are sent without being encrypted.
+ */
+public class XOauth2Mechanism extends AbstractMechanism {
+
+    private String additionalFailureInformation;
+
+    @Override
+    public int getPriority() {
+        return PRIORITY.LOWEST.getValue();
+    }
+
+    @Override
+    public String getName() {
+        return "XOAUTH2";
+    }
+
+    @Override
+    public byte[] getInitialResponse() {
+
+        String username = getUsername();
+        String password = getPassword();
+
+        if (username == null) {
+            username = "";
+        }
+
+        if (password == null) {
+            password = "";
+        }
+
+        byte[] usernameBytes = username.getBytes(StandardCharsets.UTF_8);
+        byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
+        byte[] data = new byte[usernameBytes.length + passwordBytes.length + 20];
+        System.arraycopy("user=".getBytes(StandardCharsets.US_ASCII), 0, data, 0, 5);
+        System.arraycopy(usernameBytes, 0, data, 5, usernameBytes.length);
+        data[5+usernameBytes.length] = 1;
+        System.arraycopy("auth=Bearer ".getBytes(StandardCharsets.US_ASCII), 0, data, 6+usernameBytes.length, 12);
+        System.arraycopy(passwordBytes, 0, data, 18 + usernameBytes.length, passwordBytes.length);
+        data[data.length-2] = 1;
+        data[data.length-1] = 1;
+
+        return data;
+    }
+
+    @Override
+    public byte[] getChallengeResponse(byte[] challenge) {
+        if(challenge != null && challenge.length > 0 && additionalFailureInformation == null) {
+            additionalFailureInformation = new String(challenge, StandardCharsets.UTF_8);
+        }
+        return EMPTY;
+    }
+
+    @Override
+    public boolean isApplicable(String username, String password, Principal localPrincipal) {
+        if(username != null && username.length() > 0 && password != null && password.length() > 0) {
+            try {
+                Base64.getDecoder().decode(password);
+                return true;
+            } catch (IllegalArgumentException e) {
+                return false;
+            }
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public String getAdditionalFailureInformation() {
+        return additionalFailureInformation;
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/XOauth2MechanismFactory.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/XOauth2MechanismFactory.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/XOauth2MechanismFactory.java
new file mode 100644
index 0000000..1826955
--- /dev/null
+++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/sasl/XOauth2MechanismFactory.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.jms.sasl;
+
+/**
+ * Create the Plain SASL Authentication Mechanism types.
+ */
+public class XOauth2MechanismFactory implements MechanismFactory {
+
+    @Override
+    public Mechanism createMechanism() {
+        return new XOauth2Mechanism();
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-client/src/main/resources/META-INF/services/org/apache/qpid/jms/sasl/XOAUTH2
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/main/resources/META-INF/services/org/apache/qpid/jms/sasl/XOAUTH2 b/qpid-jms-client/src/main/resources/META-INF/services/org/apache/qpid/jms/sasl/XOAUTH2
new file mode 100644
index 0000000..1f560c7
--- /dev/null
+++ b/qpid-jms-client/src/main/resources/META-INF/services/org/apache/qpid/jms/sasl/XOAUTH2
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## Licensed to the Apache Software Foundation (ASF) under one or more
+## contributor license agreements.  See the NOTICE file distributed with
+## this work for additional information regarding copyright ownership.
+## The ASF licenses this file to You under the Apache License, Version 2.0
+## (the "License"); you may not use this file except in compliance with
+## the License.  You may obtain a copy of the License at
+##
+## http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+## ---------------------------------------------------------------------------
+class=org.apache.qpid.jms.sasl.XOauth2MechanismFactory

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslIntegrationTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslIntegrationTest.java
index 3e8256d..59fb2e7 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslIntegrationTest.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/SaslIntegrationTest.java
@@ -20,15 +20,18 @@
  */
 package org.apache.qpid.jms.integration;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
+import org.apache.qpid.jms.JmsConnectionFactory;
+import org.apache.qpid.jms.test.QpidJmsTestCase;
+import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
+import org.apache.qpid.jms.transports.TransportSslOptions;
+import org.apache.qpid.jms.transports.TransportSupport;
+import org.apache.qpid.proton.amqp.Symbol;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import java.net.URLDecoder;
 import java.net.URLEncoder;
-
 import javax.jms.Connection;
 import javax.jms.ConnectionFactory;
 import javax.jms.JMSException;
@@ -36,15 +39,11 @@ import javax.jms.JMSSecurityException;
 import javax.jms.JMSSecurityRuntimeException;
 import javax.net.ssl.SSLContext;
 
-import org.apache.qpid.jms.JmsConnectionFactory;
-import org.apache.qpid.jms.test.QpidJmsTestCase;
-import org.apache.qpid.jms.test.testpeer.TestAmqpPeer;
-import org.apache.qpid.jms.transports.TransportSslOptions;
-import org.apache.qpid.jms.transports.TransportSupport;
-import org.apache.qpid.proton.amqp.Symbol;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
 
 public class SaslIntegrationTest extends QpidJmsTestCase {
 
@@ -127,6 +126,34 @@ public class SaslIntegrationTest extends QpidJmsTestCase {
         }
     }
 
+
+    @Test(timeout = 20000)
+    public void testSaslXOauth2Connection() throws Exception {
+        try (TestAmqpPeer testPeer = new TestAmqpPeer();) {
+
+            // Expect a PLAIN connection
+            String user = "user";
+            String pass = "eyB1c2VyPSJ1c2VyIiB9";
+
+            testPeer.expectSaslXOauth2(user, pass);
+            testPeer.expectOpen();
+
+            // Each connection creates a session for managing temporary destinations etc
+            testPeer.expectBegin();
+
+            ConnectionFactory factory = new JmsConnectionFactory("amqp://localhost:" + testPeer.getServerPort());
+            Connection connection = factory.createConnection(user, pass);
+            // Set a clientID to provoke the actual AMQP connection process to occur.
+            connection.setClientID("clientName");
+
+            testPeer.waitForAllHandlersToComplete(1000);
+            assertNull(testPeer.getThrowable());
+
+            testPeer.expectClose();
+            connection.close();
+        }
+    }
+
     @Test(timeout = 20000)
     public void testSaslPlainConnectionWithURIEncodedCredentials() throws Exception {
         try (TestAmqpPeer testPeer = new TestAmqpPeer();) {

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/XOauth2MechanismTest.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/XOauth2MechanismTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/XOauth2MechanismTest.java
new file mode 100644
index 0000000..adc3a8c
--- /dev/null
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/sasl/XOauth2MechanismTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.qpid.jms.sasl;
+
+import org.junit.Test;
+
+import java.nio.charset.StandardCharsets;
+import java.security.Principal;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class XOauth2MechanismTest {
+
+    @Test
+    public void testGetInitialResponseWithNullUserAndPassword() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        byte[] response = mech.getInitialResponse();
+        assertNotNull(response);
+        assertTrue(response.length > 0);
+    }
+
+    @Test
+    public void testGetChallengeResponse() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        byte[] response = mech.getChallengeResponse(new byte[1]);
+        assertNotNull(response);
+        assertTrue(response.length == 0);
+    }
+
+    @Test
+    public void testGetChallengeResponsePopulatesAdditionalDiagnosticInformation() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        byte[] response = mech.getChallengeResponse("additional data".getBytes(StandardCharsets.UTF_8));
+        assertNotNull(response);
+        assertTrue(response.length == 0);
+        assertEquals("additional data", mech.getAdditionalFailureInformation());
+    }
+
+    @Test
+    public void testIsNotApplicableWithNoCredentials() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertFalse("Should not be applicable with no credentials", mech.isApplicable(null, null, null));
+    }
+
+    @Test
+    public void testIsNotApplicableWithNoUser() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertFalse("Should not be applicable with no username", mech.isApplicable(null, "pass", null));
+    }
+
+    @Test
+    public void testIsNotApplicableWithNoToken() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertFalse("Should not be applicable with no token", mech.isApplicable("user", null, null));
+    }
+
+    @Test
+    public void testIsNotApplicableWithEmtpyUser() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertFalse("Should not be applicable with empty username", mech.isApplicable("", "pass", null));
+    }
+
+    @Test
+    public void testIsNotApplicableWithEmtpyToken() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertFalse("Should not be applicable with empty token", mech.isApplicable("user", "", null));
+    }
+
+    @Test
+    public void testIsNotApplicableWithNonBase64Token() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertFalse("Should not be applicable with non base64 token", mech.isApplicable("user", "not base 64", null));
+    }
+
+
+    @Test
+    public void testIsNotApplicableWithEmtpyUserAndToken() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertFalse("Should not be applicable with empty user and token", mech.isApplicable("", "", null));
+    }
+
+    @Test
+    public void testIsApplicableWithUserAndToken() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertTrue("Should be applicable with user and token", mech.isApplicable("user", "YmFzZSA2NA==", null));
+    }
+
+    @Test
+    public void testIsApplicableWithUserAndPasswordAndPrincipal() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertTrue("Should be applicable with user and token and principal", mech.isApplicable("user", "YmFzZSA2NA==", new Principal() {
+            @Override
+            public String getName() {
+                return "name";
+            }
+        }));
+    }
+
+    @Test
+    public void testIsEnabledByDefault() {
+        XOauth2Mechanism mech = new XOauth2Mechanism();
+
+        assertTrue("Should be enabled by default", mech.isEnabledByDefault());
+    }
+}

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
----------------------------------------------------------------------
diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
index 3b4378d..16e532a 100644
--- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
+++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/test/testpeer/TestAmqpPeer.java
@@ -140,6 +140,7 @@ public class TestAmqpPeer implements AutoCloseable
     private static final Symbol EXTERNAL = Symbol.valueOf("EXTERNAL");
     private static final Symbol PLAIN = Symbol.valueOf("PLAIN");
     private static final Symbol GSSAPI = Symbol.valueOf("GSSAPI");
+    private static final Symbol XOAUTH2 = Symbol.valueOf("XOAUTH2");
     private static final UnsignedByte SASL_OK = UnsignedByte.valueOf((byte)0);
     private static final UnsignedByte SASL_FAIL_AUTH = UnsignedByte.valueOf((byte)1);
     private static final int CONNECTION_CHANNEL = 0;
@@ -759,6 +760,25 @@ public class TestAmqpPeer implements AutoCloseable
         expectSaslAuthentication(PLAIN, initialResponseMatcher, null, true, false);
     }
 
+    public void expectSaslXOauth2(String username, String password)
+    {
+        byte[] usernameBytes = username.getBytes(StandardCharsets.UTF_8);
+        byte[] passwordBytes = password.getBytes(StandardCharsets.UTF_8);
+        byte[] data = new byte[usernameBytes.length+passwordBytes.length+20];
+
+        System.arraycopy("user=".getBytes(StandardCharsets.US_ASCII), 0, data, 0, 5);
+        System.arraycopy(usernameBytes, 0, data, 5, usernameBytes.length);
+        data[5+usernameBytes.length] = 1;
+        System.arraycopy("auth=Bearer ".getBytes(StandardCharsets.US_ASCII), 0, data, 6+usernameBytes.length, 12);
+        System.arraycopy(passwordBytes, 0, data, 18 + usernameBytes.length, passwordBytes.length);
+        data[data.length-2] = 1;
+        data[data.length-1] = 1;
+
+        Matcher<Binary> initialResponseMatcher = equalTo(new Binary(data));
+
+        expectSaslAuthentication(XOAUTH2, initialResponseMatcher, null, true, false);
+    }
+
     public void expectSaslExternal()
     {
         if(!_driverRunnable.isNeedClientCert())

http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/3d2ec9c5/qpid-jms-docs/Configuration.md
----------------------------------------------------------------------
diff --git a/qpid-jms-docs/Configuration.md b/qpid-jms-docs/Configuration.md
index 4a04e01..4add66c 100644
--- a/qpid-jms-docs/Configuration.md
+++ b/qpid-jms-docs/Configuration.md
@@ -200,7 +200,7 @@ These options apply to the behaviour of certain AMQP functionality.
 + **amqp.idleTimeout** The idle timeout in milliseconds after which the connection will be failed if the peer sends no AMQP frames. Default is 60000.
 + **amqp.vhost** The vhost to connect to. Used to populate the Sasl and Open hostname fields. Default is the main hostname from the Connection URI.
 + **amqp.saslLayer** Controls whether connections should use a SASL layer or not. Default is true.
-+ **amqp.saslMechanisms** Which SASL mechanism(s) the client should allow selection of, if offered by the server and usable with the configured credentials. Comma separated if specifying more than 1 mechanism. The clients supported mechanisms are currently EXTERNAL, SCRAM-SHA-256, SCRAM-SHA-1, CRAM-MD5, PLAIN, ANONYMOUS, and GSSAPI for Kerberos.  Default is to allow selection from all mechanisms except GSSAPI, which must be specified here to enable.
++ **amqp.saslMechanisms** Which SASL mechanism(s) the client should allow selection of, if offered by the server and usable with the configured credentials. Comma separated if specifying more than 1 mechanism. The clients supported mechanisms are currently EXTERNAL, SCRAM-SHA-256, SCRAM-SHA-1, CRAM-MD5, PLAIN, ANONYMOUS, XOAUTH2, and GSSAPI for Kerberos.  Default is to allow selection from all mechanisms except GSSAPI, which must be specified here to enable.
 + **amqp.maxFrameSize** The max-frame-size value in bytes that is advertised to the peer. Default is 1048576.
 + **amqp.drainTimeout** The time in milliseconds that the client will wait for a response from the remote when a consumer drain request is made. If no response is seen in the allotted timeout period the link will be considered failed and the associated consumer will be closed. Default is 60000.
 + **amqp.allowNonSecureRedirects** Controls whether an AMQP connection will allow for a redirect to an alternative host over a connection that is not secure when the existing connection is secure, e.g. redirecting an SSL connection to a raw TCP connection.  This value defaults to false.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org