You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ro...@apache.org on 2018/01/08 18:35:25 UTC
qpid-proton-j git commit: PROTON-1736: add a SASL listener to allow
relevant processing as frames arrive
Repository: qpid-proton-j
Updated Branches:
refs/heads/master 2d565b857 -> 17cef9ace
PROTON-1736: add a SASL listener to allow relevant processing as frames arrive
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton-j/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton-j/commit/17cef9ac
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton-j/tree/17cef9ac
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton-j/diff/17cef9ac
Branch: refs/heads/master
Commit: 17cef9ace9a7c75901d517f951ae1d4610819436
Parents: 2d565b8
Author: Robbie Gemmell <ro...@apache.org>
Authored: Mon Jan 8 18:24:44 2018 +0000
Committer: Robbie Gemmell <ro...@apache.org>
Committed: Mon Jan 8 18:24:44 2018 +0000
----------------------------------------------------------------------
.../org/apache/qpid/proton/engine/Sasl.java | 5 +
.../apache/qpid/proton/engine/SaslListener.java | 79 +++++++++
.../qpid/proton/engine/impl/SaslImpl.java | 28 +++
.../qpid/proton/systemtests/SaslTest.java | 170 +++++++++++++++++++
4 files changed, 282 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/17cef9ac/proton-j/src/main/java/org/apache/qpid/proton/engine/Sasl.java
----------------------------------------------------------------------
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/engine/Sasl.java b/proton-j/src/main/java/org/apache/qpid/proton/engine/Sasl.java
index e5ebabd..4ccbcfe 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/engine/Sasl.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/engine/Sasl.java
@@ -177,4 +177,9 @@ public interface Sasl
* that skip the SASL layer negotiation.
*/
void allowSkip(boolean allowSkip);
+
+ /**
+ * Adds a listener to receive notice of frames having arrived.
+ */
+ void setListener(SaslListener saslListener);
}
http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/17cef9ac/proton-j/src/main/java/org/apache/qpid/proton/engine/SaslListener.java
----------------------------------------------------------------------
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/engine/SaslListener.java b/proton-j/src/main/java/org/apache/qpid/proton/engine/SaslListener.java
new file mode 100644
index 0000000..d53086d
--- /dev/null
+++ b/proton-j/src/main/java/org/apache/qpid/proton/engine/SaslListener.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * 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.proton.engine;
+
+/**
+ * Listener for SASL frame arrival to facilitate relevant handling for the SASL
+ * negotiation.
+ *
+ * See the AMQP specification
+ * <a href="http://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-security-v1.0-os.html#doc-idp51040">
+ * SASL negotiation process</a> overview for related detail.
+ */
+public interface SaslListener {
+
+ /**
+ * Called when a sasl-mechanisms frame has arrived and its effect
+ * applied, indicating the offered mechanisms sent by the 'server' peer.
+ *
+ * @param sasl the Sasl object
+ * @param transport the related transport
+ */
+ void onSaslMechanisms(Sasl sasl, Transport transport);
+
+ /**
+ * Called when a sasl-init frame has arrived and its effect
+ * applied, indicating the selected mechanism and any hostname
+ * and initial-response details from the 'client' peer.
+ *
+ * @param sasl the Sasl object
+ * @param transport the related transport
+ */
+ void onSaslInit(Sasl sasl, Transport transport);
+
+ /**
+ * Called when a sasl-challenge frame has arrived and its effect
+ * applied, indicating the challenge sent by the 'server' peer.
+ *
+ * @param sasl the Sasl object
+ * @param transport the related transport
+ */
+ void onSaslChallenge(Sasl sasl, Transport transport);
+
+ /**
+ * Called when a sasl-response frame has arrived and its effect
+ * applied, indicating the response sent by the 'client' peer.
+ *
+ * @param sasl the Sasl object
+ * @param transport the related transport
+ */
+ void onSaslResponse(Sasl sasl, Transport transport);
+
+ /**
+ * Called when a sasl-outcome frame has arrived and its effect
+ * applied, indicating the outcome and any success additional-data
+ * sent by the 'server' peer.
+ *
+ * @param sasl the Sasl object
+ * @param transport the related transport
+ */
+ void onSaslOutcome(Sasl sasl, Transport transport);
+}
http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/17cef9ac/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/SaslImpl.java
----------------------------------------------------------------------
diff --git a/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/SaslImpl.java b/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/SaslImpl.java
index d6f510b..acbf5ea 100644
--- a/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/SaslImpl.java
+++ b/proton-j/src/main/java/org/apache/qpid/proton/engine/impl/SaslImpl.java
@@ -42,6 +42,7 @@ import org.apache.qpid.proton.codec.AMQPDefinedTypes;
import org.apache.qpid.proton.codec.DecoderImpl;
import org.apache.qpid.proton.codec.EncoderImpl;
import org.apache.qpid.proton.engine.Sasl;
+import org.apache.qpid.proton.engine.SaslListener;
import org.apache.qpid.proton.engine.Transport;
import org.apache.qpid.proton.engine.TransportException;
@@ -85,6 +86,8 @@ public class SaslImpl implements Sasl, SaslFrameBody.SaslFrameBodyHandler<Void>,
private Role _role;
private boolean _allowSkip = true;
+ private SaslListener _saslListener;
+
/**
* @param maxFrameSize the size of the input and output buffers
* returned by {@link SaslTransportWrapper#getInputBuffer()} and
@@ -346,6 +349,10 @@ public class SaslImpl implements Sasl, SaslFrameBody.SaslFrameBodyHandler<Void>,
{
setPending(saslInit.getInitialResponse().asByteBuffer());
}
+
+ if(_saslListener != null) {
+ _saslListener.onSaslInit(this, _transport);
+ }
}
@Override
@@ -353,6 +360,10 @@ public class SaslImpl implements Sasl, SaslFrameBody.SaslFrameBodyHandler<Void>,
{
checkRole(Role.SERVER);
setPending(saslResponse.getResponse() == null ? null : saslResponse.getResponse().asByteBuffer());
+
+ if(_saslListener != null) {
+ _saslListener.onSaslResponse(this, _transport);
+ }
}
@Override
@@ -382,6 +393,10 @@ public class SaslImpl implements Sasl, SaslFrameBody.SaslFrameBodyHandler<Void>,
}
checkRole(Role.CLIENT);
_mechanisms = saslMechanisms.getSaslServerMechanisms();
+
+ if(_saslListener != null) {
+ _saslListener.onSaslMechanisms(this, _transport);
+ }
}
@Override
@@ -389,6 +404,10 @@ public class SaslImpl implements Sasl, SaslFrameBody.SaslFrameBodyHandler<Void>,
{
checkRole(Role.CLIENT);
setPending(saslChallenge.getChallenge() == null ? null : saslChallenge.getChallenge().asByteBuffer());
+
+ if(_saslListener != null) {
+ _saslListener.onSaslChallenge(this, _transport);
+ }
}
@Override
@@ -416,6 +435,10 @@ public class SaslImpl implements Sasl, SaslFrameBody.SaslFrameBodyHandler<Void>,
{
_logger.fine("Handled outcome: " + this);
}
+
+ if(_saslListener != null) {
+ _saslListener.onSaslOutcome(this, _transport);
+ }
}
private SaslState classifyStateFromOutcome(SaslOutcome outcome)
@@ -742,4 +765,9 @@ public class SaslImpl implements Sasl, SaslFrameBody.SaslFrameBodyHandler<Void>,
_hostname = hostname;
}
+
+ @Override
+ public void setListener(SaslListener saslListener) {
+ _saslListener = saslListener;
+ }
}
http://git-wip-us.apache.org/repos/asf/qpid-proton-j/blob/17cef9ac/proton-j/src/test/java/org/apache/qpid/proton/systemtests/SaslTest.java
----------------------------------------------------------------------
diff --git a/proton-j/src/test/java/org/apache/qpid/proton/systemtests/SaslTest.java b/proton-j/src/test/java/org/apache/qpid/proton/systemtests/SaslTest.java
index 12f5143..406e06f 100644
--- a/proton-j/src/test/java/org/apache/qpid/proton/systemtests/SaslTest.java
+++ b/proton-j/src/test/java/org/apache/qpid/proton/systemtests/SaslTest.java
@@ -21,17 +21,22 @@ package org.apache.qpid.proton.systemtests;
import static org.apache.qpid.proton.systemtests.TestLoggingHelper.bold;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import org.junit.Test;
import org.apache.qpid.proton.Proton;
import org.apache.qpid.proton.engine.Sasl;
+import org.apache.qpid.proton.engine.SaslListener;
+import org.apache.qpid.proton.engine.Transport;
import org.apache.qpid.proton.engine.Sasl.SaslOutcome;
public class SaslTest extends EngineTestBase
@@ -39,6 +44,7 @@ public class SaslTest extends EngineTestBase
private static final Logger LOGGER = Logger.getLogger(SaslTest.class.getName());
private static final String TESTMECH1 = "TESTMECH1";
private static final String TESTMECH2 = "TESTMECH2";
+ private static final byte[] INITIAL_RESPONSE_BYTES = "initial-response-bytes".getBytes(StandardCharsets.UTF_8);
private static final byte[] CHALLENGE_BYTES = "challenge-bytes".getBytes(StandardCharsets.UTF_8);
private static final byte[] RESPONSE_BYTES = "response-bytes".getBytes(StandardCharsets.UTF_8);
private static final byte[] ADDITIONAL_DATA_BYTES = "additional-data-bytes".getBytes(StandardCharsets.UTF_8);
@@ -386,4 +392,168 @@ public class SaslTest extends EngineTestBase
return bytes;
}
+
+ @Test
+ public void testSaslNegotiationUsingListener() throws Exception
+ {
+ getClient().transport = Proton.transport();
+ getServer().transport = Proton.transport();
+
+ AtomicBoolean mechanismsReceived = new AtomicBoolean();
+ AtomicBoolean challengeReceived = new AtomicBoolean();
+ AtomicBoolean outcomeReceived = new AtomicBoolean();
+
+ Sasl clientSasl = getClient().transport.sasl();
+ clientSasl.client();
+ clientSasl.setListener(new ClientSaslHandling(mechanismsReceived, challengeReceived, outcomeReceived));
+
+ AtomicBoolean initReceived = new AtomicBoolean();
+ AtomicBoolean responseReceived = new AtomicBoolean();
+
+ Sasl serverSasl = getServer().transport.sasl();
+ serverSasl.server();
+ serverSasl.setMechanisms(TESTMECH1, TESTMECH2);
+ serverSasl.setListener(new ServerSaslHandling(initReceived, responseReceived));
+
+ pumpClientToServer();
+ pumpServerToClient();
+
+ assertTrue("mechanisms were not received by client", mechanismsReceived.get());
+ assertFalse("init was received by server", initReceived.get());
+
+ pumpClientToServer();
+
+ assertTrue("init was not received by server", initReceived.get());
+ assertFalse("challenge was received by client", challengeReceived.get());
+
+ pumpServerToClient();
+
+ assertTrue("challenge was not received by client", challengeReceived.get());
+ assertFalse("response was received by server", responseReceived.get());
+
+ pumpClientToServer();
+
+ assertTrue("response was received by server", responseReceived.get());
+ assertFalse("outcome was received by client", outcomeReceived.get());
+
+ pumpServerToClient();
+
+ assertTrue("outcome was received by client", outcomeReceived.get());
+
+ assertEquals("Unexpected SASL outcome at client", SaslOutcome.PN_SASL_OK, clientSasl.getOutcome());
+ }
+
+ private static class ServerSaslHandling implements SaslListener
+ {
+ AtomicBoolean initReceived = new AtomicBoolean();
+ AtomicBoolean responseReceived = new AtomicBoolean();
+
+ public ServerSaslHandling(AtomicBoolean initReceived, AtomicBoolean responseReceived)
+ {
+ this.initReceived = initReceived;
+ this.responseReceived = responseReceived;
+ }
+
+ @Override
+ public void onSaslInit(Sasl s, Transport t)
+ {
+ assertArrayEquals("Server should now know the client's chosen mechanism.",
+ new String[]{TESTMECH1}, s.getRemoteMechanisms());
+
+ byte[] serverReceivedInitialBytes = new byte[s.pending()];
+ s.recv(serverReceivedInitialBytes, 0, serverReceivedInitialBytes.length);
+
+ assertArrayEquals("Server should now know the client's initial response.",
+ INITIAL_RESPONSE_BYTES, serverReceivedInitialBytes);
+
+ s.send(CHALLENGE_BYTES, 0, CHALLENGE_BYTES.length);
+
+ assertFalse("Should not have already received init", initReceived.getAndSet(true));
+ }
+
+ @Override
+ public void onSaslResponse(Sasl s, Transport t)
+ {
+ byte[] serverReceivedResponseBytes = new byte[s.pending()];
+ s.recv(serverReceivedResponseBytes, 0, serverReceivedResponseBytes.length);
+
+ assertArrayEquals("Server should now know the client's response", RESPONSE_BYTES, serverReceivedResponseBytes);
+
+ s.send(ADDITIONAL_DATA_BYTES, 0, ADDITIONAL_DATA_BYTES.length);
+ s.done(SaslOutcome.PN_SASL_OK);
+
+ assertFalse("Should not have already received response", responseReceived.getAndSet(true));
+ }
+
+ @Override
+ public void onSaslMechanisms(Sasl s, Transport t) { }
+
+ @Override
+ public void onSaslChallenge(Sasl s, Transport t) { }
+
+ @Override
+ public void onSaslOutcome(Sasl s, Transport t) { }
+ }
+
+ private static class ClientSaslHandling implements SaslListener
+ {
+ AtomicBoolean mechanismsReceived = new AtomicBoolean();
+ AtomicBoolean challengeReceived = new AtomicBoolean();
+ AtomicBoolean outcomeReceived = new AtomicBoolean();
+
+ public ClientSaslHandling(AtomicBoolean mechanismsReceived, AtomicBoolean challengeReceived, AtomicBoolean outcomeReceived)
+ {
+ this.mechanismsReceived = mechanismsReceived;
+ this.challengeReceived = challengeReceived;
+ this.outcomeReceived = outcomeReceived;
+ }
+
+ @Override
+ public void onSaslMechanisms(Sasl s, Transport t)
+ {
+ assertArrayEquals("Client should now know the server's mechanisms.",
+ new String[]{TESTMECH1, TESTMECH2}, s.getRemoteMechanisms());
+ assertEquals("Unexpected SASL outcome at client", SaslOutcome.PN_SASL_NONE, s.getOutcome());
+
+ s.setMechanisms(TESTMECH1);
+ s.send(INITIAL_RESPONSE_BYTES, 0, INITIAL_RESPONSE_BYTES.length);
+
+ assertFalse("Should not have already received mechanisms", mechanismsReceived.getAndSet(true));
+ }
+
+ @Override
+ public void onSaslChallenge(Sasl s, Transport t)
+ {
+ byte[] clientReceivedChallengeBytes = new byte[s.pending()];
+ s.recv(clientReceivedChallengeBytes, 0, clientReceivedChallengeBytes.length);
+
+ assertEquals("Unexpected SASL outcome at client", SaslOutcome.PN_SASL_NONE, s.getOutcome());
+ assertArrayEquals("Client should now know the server's challenge",
+ CHALLENGE_BYTES, clientReceivedChallengeBytes);
+
+ s.send(RESPONSE_BYTES, 0, RESPONSE_BYTES.length);
+
+ assertFalse("Should not have already received challenge", challengeReceived.getAndSet(true));
+ }
+
+ @Override
+ public void onSaslOutcome(Sasl s, Transport t)
+ {
+ assertEquals("Unexpected SASL outcome at client", SaslOutcome.PN_SASL_OK, s.getOutcome());
+
+ byte[] clientReceivedAdditionalBytes = new byte[s.pending()];
+ s.recv(clientReceivedAdditionalBytes, 0, clientReceivedAdditionalBytes.length);
+
+ assertArrayEquals("Client should now know the server's outcome additional data", clientReceivedAdditionalBytes,
+ clientReceivedAdditionalBytes);
+
+ assertFalse("Should not have already received outcome", outcomeReceived.getAndSet(true));
+ }
+
+ @Override
+ public void onSaslInit(Sasl s, Transport t) { }
+
+ @Override
+ public void onSaslResponse(Sasl s, Transport t) { }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org