Repository: qpid-jms Updated Branches: refs/heads/master cb5abb046 -> 20b458567
QPIDJMS-163 Add support for setting the authenticated User ID value from the connection into messages that are sent. Default is to not populate the user id portion of the message and can be enabled via the configuration option jms.populateJMSXUserID=true Project: http://git-wip-us.apache.org/repos/asf/qpid-jms/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-jms/commit/20b45856 Tree: http://git-wip-us.apache.org/repos/asf/qpid-jms/tree/20b45856 Diff: http://git-wip-us.apache.org/repos/asf/qpid-jms/diff/20b45856 Branch: refs/heads/master Commit: 20b458567a7b466bda22c5be7b26f367c8a3e778 Parents: cb5abb0 Author: Timothy Bish <[email protected]> Authored: Wed Mar 30 18:04:34 2016 -0400 Committer: Timothy Bish <[email protected]> Committed: Wed Mar 30 18:04:34 2016 -0400 ---------------------------------------------------------------------- .../java/org/apache/qpid/jms/JmsConnection.java | 12 + .../apache/qpid/jms/JmsConnectionFactory.java | 19 ++ .../java/org/apache/qpid/jms/JmsSession.java | 15 + .../message/JmsMessagePropertyIntercepter.java | 2 +- .../jms/message/facade/JmsMessageFacade.java | 21 ++ .../apache/qpid/jms/meta/JmsConnectionInfo.java | 20 ++ .../amqp/message/AmqpJmsMessageFacade.java | 26 +- .../qpid/jms/JmsConnectionFactoryTest.java | 13 + .../integration/ProducerIntegrationTest.java | 298 +++++++++++++++++++ .../facade/test/JmsTestMessageFacade.java | 14 + .../amqp/message/AmqpJmsMessageFacadeTest.java | 94 +++++- 11 files changed, 527 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java index a26517b..ee0bba4 100644 --- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnection.java @@ -953,6 +953,10 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection connectionInfo.setUsername(username);; } + byte[] getEncodedUsername() { + return connectionInfo.getEncodedUsername(); + } + public String getPassword() { return connectionInfo.getPassword(); } @@ -1016,6 +1020,14 @@ public class JmsConnection implements AutoCloseable, Connection, TopicConnection this.messageIDBuilder = messageIDBuilder; } + public boolean isPopulateJMSXUserID() { + return connectionInfo.isPopulateJMSXUserID(); + } + + public void setPopulateJMSXUserID(boolean populateJMSXUserID) { + connectionInfo.setPopulateJMSXUserID(populateJMSXUserID); + } + //----- Async event handlers ---------------------------------------------// @Override http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java index 60b9628..bff10cc 100644 --- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsConnectionFactory.java @@ -70,6 +70,7 @@ public class JmsConnectionFactory extends JNDIStorable implements ConnectionFact private boolean localMessageExpiry = true; private boolean receiveLocalOnly; private boolean receiveNoWaitLocalOnly; + private boolean populateJMSXUserID; private String queuePrefix = null; private String topicPrefix = null; private boolean validatePropertyNames = true; @@ -770,6 +771,24 @@ public class JmsConnectionFactory extends JNDIStorable implements ConnectionFact this.receiveNoWaitLocalOnly = receiveNoWaitLocalOnly; } + public boolean isPopulateJMSXUserID() { + return populateJMSXUserID; + } + + /** + * Controls whether message sent from the Connection will have the JMSXUserID message + * property populated with the authenticated user ID of the Connection. When false all + * messages sent from the Connection will not carry any value in the JMSXUserID property + * regardless of it being manually set on the Message to prevent a client spoofing the + * JMSXUserID value. + * + * @param populateJMSXUserID + * true if message sent from this connection should have the JMSXUserID value populated. + */ + public void setPopulateJMSXUserID(boolean populateJMSXUserID) { + this.populateJMSXUserID = populateJMSXUserID; + } + //----- Static Methods ---------------------------------------------------// /** http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java index d99b0b2..a9ef004 100644 --- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/JmsSession.java @@ -653,12 +653,27 @@ public class JmsSession implements AutoCloseable, Session, QueueSession, TopicSe if (isJmsMessage) { JmsMessage jmsMessage = (JmsMessage) original; jmsMessage.getFacade().setProviderMessageIdObject(messageId); + + if (connection.isPopulateJMSXUserID()) { + jmsMessage.getFacade().setUserIdBytes(connection.getEncodedUsername()); + } else { + // Prevent user spoofing the user ID value. + jmsMessage.getFacade().setUserId(null); + } + copy = jmsMessage.copy(); } else { copy = JmsMessageTransformation.transformMessage(connection, original); copy.getFacade().setProviderMessageIdObject(messageId); copy.setJMSDestination(destination); + if (connection.isPopulateJMSXUserID()) { + copy.getFacade().setUserIdBytes(connection.getEncodedUsername()); + } else { + // Prevent user spoofing the user ID value. + copy.getFacade().setUserId(null); + } + // If the original was a foreign message, we still need to update it // with the properly encoded Message ID String, get it from the copy. original.setJMSMessageID(copy.getJMSMessageID()); http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java index d02e633..64d400d 100644 --- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/JmsMessagePropertyIntercepter.java @@ -567,7 +567,7 @@ public class JmsMessagePropertyIntercepter { @Override public void setProperty(JmsMessage message, Object value) throws JMSException { - if (!(value instanceof String)) { + if (value != null && !(value instanceof String)) { throw new JMSException("Property JMSXUserID cannot be set from a " + value.getClass().getName() + "."); } message.getFacade().setUserId((String) value); http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java index 41ef9c9..35be150 100644 --- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/message/facade/JmsMessageFacade.java @@ -360,6 +360,27 @@ public interface JmsMessageFacade { void setUserId(String userId); /** + * Gets the set user ID of the message in raw bytes form. If no ID was + * set then this method may return null or an empty byte array. + * + * @return a byte array containing the user ID value in raw form. + * + * @throws JMSException if an error occurs while accessing the property. + */ + byte[] getUserIdBytes() throws JMSException; + + /** + * Sets the user ID of the message in raw byte form. Setting the value + * as null or an empty byte array will clear any previously set value. If the + * underlying protocol cannot convert or map the given byte value to it's own + * internal representation it should throw a JMSException indicating the error. + * + * @param userId + * the byte array to use to set the message user ID. + */ + void setUserIdBytes(byte[] userId); + + /** * Gets the Group ID that this message is assigned to. * * @return the Group ID this message was sent in. http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/main/java/org/apache/qpid/jms/meta/JmsConnectionInfo.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/meta/JmsConnectionInfo.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/meta/JmsConnectionInfo.java index 1c8fa7f..d97d33d 100644 --- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/meta/JmsConnectionInfo.java +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/meta/JmsConnectionInfo.java @@ -17,6 +17,7 @@ package org.apache.qpid.jms.meta; import java.net.URI; +import java.nio.charset.Charset; import org.apache.qpid.jms.JmsPrefetchPolicy; import org.apache.qpid.jms.JmsRedeliveryPolicy; @@ -48,6 +49,7 @@ public final class JmsConnectionInfo implements JmsResource, Comparable<JmsConne private boolean receiveNoWaitLocalOnly; private boolean localMessagePriority; private boolean localMessageExpiry; + private boolean populateJMSXUserID; private long sendTimeout = DEFAULT_SEND_TIMEOUT; private long requestTimeout = DEFAULT_REQUEST_TIMEOUT; private long connectTimeout = DEFAULT_CONNECT_TIMEOUT; @@ -58,6 +60,8 @@ public final class JmsConnectionInfo implements JmsResource, Comparable<JmsConne private JmsPrefetchPolicy prefetchPolicy = new JmsPrefetchPolicy(); private JmsRedeliveryPolicy redeliveryPolicy = new JmsRedeliveryPolicy(); + private volatile byte[] encodedUserId; + public JmsConnectionInfo(JmsConnectionId connectionId) { if (connectionId == null) { throw new IllegalArgumentException("ConnectionId cannot be null"); @@ -260,6 +264,22 @@ public final class JmsConnectionInfo implements JmsResource, Comparable<JmsConne this.redeliveryPolicy = redeliveryPolicy.copy(); } + public boolean isPopulateJMSXUserID() { + return populateJMSXUserID; + } + + public void setPopulateJMSXUserID(boolean populateMessageUserID) { + this.populateJMSXUserID = populateMessageUserID; + } + + public byte[] getEncodedUsername() { + if (encodedUserId == null && username != null) { + encodedUserId = username.getBytes(Charset.forName("UTF-8")); + } + + return encodedUserId; + } + @Override public String toString() { return "JmsConnectionInfo { " + getId() + http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java index a337036..a739999 100644 --- a/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java +++ b/qpid-jms-client/src/main/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacade.java @@ -342,7 +342,7 @@ public class AmqpJmsMessageFacade implements JmsMessageFacade { @Override public void setProviderMessageIdObject(Object messageId) { - message.setMessageId(messageId); + message.setMessageId(messageId); } @Override @@ -684,7 +684,29 @@ public class AmqpJmsMessageFacade implements JmsMessageFacade { bytes = userId.getBytes(UTF8); } - message.setUserId(bytes); + if (bytes == null) { + if (message.getProperties() != null) { + message.getProperties().setUserId(null); + } + } else { + message.setUserId(bytes); + } + } + + @Override + public byte[] getUserIdBytes() { + return message.getUserId(); + } + + @Override + public void setUserIdBytes(byte[] userId) { + if (userId == null || userId.length == 0) { + if (message.getProperties() != null) { + message.getProperties().setUserId(null); + } + } else { + message.setUserId(userId); + } } @Override http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFactoryTest.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFactoryTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFactoryTest.java index 679fff4..965e990 100644 --- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFactoryTest.java +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/JmsConnectionFactoryTest.java @@ -447,4 +447,17 @@ public class JmsConnectionFactoryTest extends QpidJmsTestCase { LOG.debug("Caught Ex -> ", jmse); } } + + @Test(timeout = 5000) + public void testURIOptionPopulateJMSXUserID() throws Exception { + JmsConnectionFactory factory = new JmsConnectionFactory( + "amqp://127.0.0.1:5672?jms.populateJMSXUserID=true"); + + assertTrue(factory.isPopulateJMSXUserID()); + + factory = new JmsConnectionFactory( + "amqp://127.0.0.1:5672?jms.populateJMSXUserID=false"); + + assertFalse(factory.isPopulateJMSXUserID()); + } } http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java index a9b9a45..4d38dd6 100644 --- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/integration/ProducerIntegrationTest.java @@ -31,7 +31,12 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Collections; import java.util.Date; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -51,6 +56,7 @@ import org.apache.qpid.jms.JmsConnection; import org.apache.qpid.jms.JmsConnectionFactory; import org.apache.qpid.jms.JmsOperationTimedOutException; import org.apache.qpid.jms.JmsSendTimedOutException; +import org.apache.qpid.jms.message.foreign.ForeignJmsMessage; import org.apache.qpid.jms.provider.amqp.message.AmqpMessageSupport; import org.apache.qpid.jms.test.QpidJmsTestCase; import org.apache.qpid.jms.test.Wait; @@ -65,6 +71,7 @@ import org.apache.qpid.jms.test.testpeer.matchers.sections.MessageHeaderSectionM import org.apache.qpid.jms.test.testpeer.matchers.sections.MessagePropertiesSectionMatcher; import org.apache.qpid.jms.test.testpeer.matchers.sections.TransferPayloadCompositeMatcher; import org.apache.qpid.jms.test.testpeer.matchers.types.EncodedAmqpValueMatcher; +import org.apache.qpid.proton.amqp.Binary; import org.apache.qpid.proton.amqp.UnsignedByte; import org.apache.qpid.proton.amqp.UnsignedInteger; import org.hamcrest.Matcher; @@ -1293,4 +1300,295 @@ public class ProducerIntegrationTest extends QpidJmsTestCase { testPeer.waitForAllHandlersToComplete(1000); } } + + @Test(timeout = 20000) + public void testUserIdSetWhenConfiguredForInclusion() throws Exception { + try (TestAmqpPeer testPeer = new TestAmqpPeer();) { + + // Expect a PLAIN connection + String user = "user"; + String pass = "qwerty123456"; + + testPeer.expectSaslPlainConnect(user, pass, null, null); + testPeer.expectBegin(); + testPeer.expectBegin(); + testPeer.expectSenderAttach(); + + JmsConnectionFactory factory = new JmsConnectionFactory( + "amqp://localhost:" + testPeer.getServerPort()); + factory.setPopulateJMSXUserID(true); + + Connection connection = factory.createConnection(user, pass); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("TestQueue"); + MessageProducer producer = session.createProducer(queue); + + Binary binaryUserId = new Binary(user.getBytes(Charset.forName("UTF-8"))); + MessagePropertiesSectionMatcher propertiesMatcher = new MessagePropertiesSectionMatcher(true); + propertiesMatcher.withUserId(equalTo(binaryUserId)); + MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true); + MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true); + TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher(); + messageMatcher.setPropertiesMatcher(propertiesMatcher); + messageMatcher.setHeadersMatcher(headersMatcher); + messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher); + + testPeer.expectTransfer(messageMatcher); + + producer.send(session.createMessage()); + + testPeer.expectClose(); + connection.close(); + + testPeer.waitForAllHandlersToComplete(1000); + } + } + + @Test(timeout = 20000) + public void testUserIdNotSetWhenNotConfiguredForInclusion() throws Exception { + try (TestAmqpPeer testPeer = new TestAmqpPeer();) { + + // Expect a PLAIN connection + String user = "user"; + String pass = "qwerty123456"; + + testPeer.expectSaslPlainConnect(user, pass, null, null); + testPeer.expectBegin(); + testPeer.expectBegin(); + testPeer.expectSenderAttach(); + + JmsConnectionFactory factory = new JmsConnectionFactory( + "amqp://localhost:" + testPeer.getServerPort()); + factory.setPopulateJMSXUserID(false); + + Connection connection = factory.createConnection(user, pass); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("TestQueue"); + MessageProducer producer = session.createProducer(queue); + + MessagePropertiesSectionMatcher propertiesMatcher = new MessagePropertiesSectionMatcher(true); + propertiesMatcher.withUserId(nullValue()); + MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true); + MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true); + TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher(); + messageMatcher.setPropertiesMatcher(propertiesMatcher); + messageMatcher.setHeadersMatcher(headersMatcher); + messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher); + + testPeer.expectTransfer(messageMatcher); + + producer.send(session.createMessage()); + + testPeer.expectClose(); + connection.close(); + + testPeer.waitForAllHandlersToComplete(1000); + } + } + + @Test(timeout = 20000) + public void testUserIdNotSpoofedWhenConfiguredForInclusion() throws Exception { + try (TestAmqpPeer testPeer = new TestAmqpPeer();) { + + // Expect a PLAIN connection + String user = "user"; + String pass = "qwerty123456"; + + testPeer.expectSaslPlainConnect(user, pass, null, null); + testPeer.expectBegin(); + testPeer.expectBegin(); + testPeer.expectSenderAttach(); + + JmsConnectionFactory factory = new JmsConnectionFactory( + "amqp://localhost:" + testPeer.getServerPort()); + factory.setPopulateJMSXUserID(true); + + Connection connection = factory.createConnection(user, pass); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("TestQueue"); + MessageProducer producer = session.createProducer(queue); + + Binary binaryUserId = new Binary(user.getBytes(Charset.forName("UTF-8"))); + MessagePropertiesSectionMatcher propertiesMatcher = new MessagePropertiesSectionMatcher(true); + propertiesMatcher.withUserId(equalTo(binaryUserId)); + MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true); + MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true); + TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher(); + messageMatcher.setPropertiesMatcher(propertiesMatcher); + messageMatcher.setHeadersMatcher(headersMatcher); + messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher); + + testPeer.expectTransfer(messageMatcher); + + Message message = session.createMessage(); + message.setStringProperty("JMSXUserID", "spoofed"); + + producer.send(message); + + testPeer.expectClose(); + connection.close(); + + testPeer.waitForAllHandlersToComplete(1000); + } + } + + @Test(timeout = 20000) + public void testUserIdNotSpoofedWhenNotConfiguredForInclusion() throws Exception { + try (TestAmqpPeer testPeer = new TestAmqpPeer();) { + + // Expect a PLAIN connection + String user = "user"; + String pass = "qwerty123456"; + + testPeer.expectSaslPlainConnect(user, pass, null, null); + testPeer.expectBegin(); + testPeer.expectBegin(); + testPeer.expectSenderAttach(); + + JmsConnectionFactory factory = new JmsConnectionFactory( + "amqp://localhost:" + testPeer.getServerPort()); + factory.setPopulateJMSXUserID(false); + + Connection connection = factory.createConnection(user, pass); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("TestQueue"); + MessageProducer producer = session.createProducer(queue); + + MessagePropertiesSectionMatcher propertiesMatcher = new MessagePropertiesSectionMatcher(true); + propertiesMatcher.withUserId(nullValue()); + MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true); + MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true); + TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher(); + messageMatcher.setPropertiesMatcher(propertiesMatcher); + messageMatcher.setHeadersMatcher(headersMatcher); + messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher); + + testPeer.expectTransfer(messageMatcher); + + Message message = session.createMessage(); + message.setStringProperty("JMSXUserID", "spoofed"); + + producer.send(message); + + testPeer.expectClose(); + connection.close(); + + testPeer.waitForAllHandlersToComplete(1000); + } + } + + private class CustomForeignMessage extends ForeignJmsMessage { + + @Override + public Enumeration<?> getPropertyNames() throws JMSException { + Enumeration<?> properties = super.getPropertyNames(); + + Set<Object> names = new HashSet<Object>(); + while (properties.hasMoreElements()) { + names.add(properties.nextElement()); + } + + names.add("JMSXUserID"); + + return Collections.enumeration(names); + } + + @Override + public Object getObjectProperty(String name) throws JMSException { + if (name.equals("JMSXUserID")) { + return "spoofed"; + } + + return message.getObjectProperty(name); + } + } + + @Test(timeout = 20000) + public void testUserIdNotSpoofedWhenConfiguredForInclusionWithForgeinMessage() throws Exception { + try (TestAmqpPeer testPeer = new TestAmqpPeer();) { + + // Expect a PLAIN connection + String user = "user"; + String pass = "qwerty123456"; + + testPeer.expectSaslPlainConnect(user, pass, null, null); + testPeer.expectBegin(); + testPeer.expectBegin(); + testPeer.expectSenderAttach(); + + JmsConnectionFactory factory = new JmsConnectionFactory( + "amqp://localhost:" + testPeer.getServerPort()); + factory.setPopulateJMSXUserID(true); + + Connection connection = factory.createConnection(user, pass); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("TestQueue"); + MessageProducer producer = session.createProducer(queue); + + Binary binaryUserId = new Binary(user.getBytes(Charset.forName("UTF-8"))); + MessagePropertiesSectionMatcher propertiesMatcher = new MessagePropertiesSectionMatcher(true); + propertiesMatcher.withUserId(equalTo(binaryUserId)); + MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true); + MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true); + TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher(); + messageMatcher.setPropertiesMatcher(propertiesMatcher); + messageMatcher.setHeadersMatcher(headersMatcher); + messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher); + + testPeer.expectTransfer(messageMatcher); + + Message message = new CustomForeignMessage(); + message.setStringProperty("JMSXUserID", "spoofed"); + + producer.send(message); + + testPeer.expectClose(); + connection.close(); + + testPeer.waitForAllHandlersToComplete(1000); + } + } + + @Test(timeout = 20000) + public void testUserIdNotSpoofedWhenNotConfiguredForInclusionWithForeignMessage() throws Exception { + try (TestAmqpPeer testPeer = new TestAmqpPeer();) { + + // Expect a PLAIN connection + String user = "user"; + String pass = "qwerty123456"; + + testPeer.expectSaslPlainConnect(user, pass, null, null); + testPeer.expectBegin(); + testPeer.expectBegin(); + testPeer.expectSenderAttach(); + + JmsConnectionFactory factory = new JmsConnectionFactory( + "amqp://localhost:" + testPeer.getServerPort()); + factory.setPopulateJMSXUserID(false); + + Connection connection = factory.createConnection(user, pass); + Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("TestQueue"); + MessageProducer producer = session.createProducer(queue); + + MessagePropertiesSectionMatcher propertiesMatcher = new MessagePropertiesSectionMatcher(true); + propertiesMatcher.withUserId(nullValue()); + MessageHeaderSectionMatcher headersMatcher = new MessageHeaderSectionMatcher(true); + MessageAnnotationsSectionMatcher msgAnnotationsMatcher = new MessageAnnotationsSectionMatcher(true); + TransferPayloadCompositeMatcher messageMatcher = new TransferPayloadCompositeMatcher(); + messageMatcher.setPropertiesMatcher(propertiesMatcher); + messageMatcher.setHeadersMatcher(headersMatcher); + messageMatcher.setMessageAnnotationsMatcher(msgAnnotationsMatcher); + + testPeer.expectTransfer(messageMatcher); + + Message message = new CustomForeignMessage(); + producer.send(message); + + testPeer.expectClose(); + connection.close(); + + testPeer.waitForAllHandlersToComplete(1000); + } + } } http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java index 72d1656..3a6bf93 100644 --- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/message/facade/test/JmsTestMessageFacade.java @@ -313,6 +313,20 @@ public class JmsTestMessageFacade implements JmsMessageFacade { } @Override + public byte[] getUserIdBytes() throws JMSException { + return userId != null ? userId.getBytes(Charset.forName("UTF-8")) : null; + } + + @Override + public void setUserIdBytes(byte[] userId) { + if (userId != null) { + this.userId = new String(userId, Charset.forName("UTF-8")); + } else { + this.userId = null; + } + } + + @Override public String getGroupId() { return this.groupId; } http://git-wip-us.apache.org/repos/asf/qpid-jms/blob/20b45856/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java ---------------------------------------------------------------------- diff --git a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java index 97e8b16..597d01f 100644 --- a/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java +++ b/qpid-jms-client/src/test/java/org/apache/qpid/jms/provider/amqp/message/AmqpJmsMessageFacadeTest.java @@ -558,7 +558,7 @@ public class AmqpJmsMessageFacadeTest extends AmqpJmsMessageTypesTestCase { } /** - * Check that setting UserId null on the message causes any existing value to be cleared + * Check that setting GroupId null on the message causes any existing value to be cleared * * @throws Exception if an error occurs during the test. */ @@ -754,8 +754,6 @@ public class AmqpJmsMessageFacadeTest extends AmqpJmsMessageTypesTestCase { AmqpJmsMessageFacade amqpMessageFacade = createNewMessageFacade(); amqpMessageFacade.setGroupSequence(5); - - // TODO amqpMessageFacade.setGroupSequence(0); // assertNull("underlying message should have no groupSequence field value", amqpMessageFacade.getAmqpMessage().getProperties().getGroupSequence()); @@ -765,7 +763,7 @@ public class AmqpJmsMessageFacadeTest extends AmqpJmsMessageTypesTestCase { @Test public void testClearGroupSequenceOnMessageWithoutExistingGroupSequence() { AmqpJmsMessageFacade amqpMessageFacade = createNewMessageFacade(); - // TODO + amqpMessageFacade.setGroupSequence(0); assertNull("underlying message should still have no properties setion", amqpMessageFacade.getAmqpMessage().getProperties()); @@ -1430,6 +1428,94 @@ public class AmqpJmsMessageFacadeTest extends AmqpJmsMessageTypesTestCase { assertNull("userid not as expected", amqpMessageFacade.getUserId()); } + @Test + public void testClearUserIdWithNoExistingProperties() { + AmqpJmsMessageFacade amqpMessageFacade = createNewMessageFacade(); + + amqpMessageFacade.setUserId(null); + + assertNull("underlying message should still have no properties setion", amqpMessageFacade.getAmqpMessage().getProperties()); + assertEquals("UserId should be null", null, amqpMessageFacade.getUserId()); + } + + // --- user-id-bytes field --- + + @Test + public void testGetUserIdBytesIsNullForNewMessage() { + AmqpJmsMessageFacade amqpMessageFacade = createNewMessageFacade(); + + assertNull("expected userid bytes to be null on new message", amqpMessageFacade.getUserIdBytes()); + } + + @Test + public void testGetUserIdBytesOnReceievedMessage() throws Exception { + String userIdString = "testValue"; + byte[] bytes = userIdString.getBytes("UTF-8"); + + Message message = Proton.message(); + + message.setUserId(bytes); + + Properties props = new Properties(); + props.setUserId(new Binary(bytes)); + message.setProperties(props); + + AmqpJmsMessageFacade amqpMessageFacade = createReceivedMessageFacade(createMockAmqpConsumer(), message); + + assertNotNull("Expected a userid on received message", amqpMessageFacade.getUserIdBytes()); + assertArrayEquals("Incorrect userid bytes value received", bytes, amqpMessageFacade.getUserIdBytes()); + } + + /** + * Check that setting UserId on the message causes creation of the underlying properties + * section with the expected value. New messages lack the properties section section, + * as tested by {@link #testNewMessageHasNoUnderlyingPropertiesSection()}. + * + * @throws Exception if an error occurs during the test. + */ + @Test + public void testSetUserIdBytesOnNewMessage() throws Exception { + String userIdString = "testValue"; + byte[] bytes = userIdString.getBytes("UTF-8"); + AmqpJmsMessageFacade amqpMessageFacade = createNewMessageFacade(); + + amqpMessageFacade.setUserIdBytes(bytes); + + assertNotNull("properties section was not created", amqpMessageFacade.getAmqpMessage().getProperties()); + assertTrue("bytes were not set as expected for userid", Arrays.equals(bytes, amqpMessageFacade.getAmqpMessage().getProperties().getUserId().getArray())); + assertArrayEquals("userid bytes not as expected", bytes, amqpMessageFacade.getUserIdBytes()); + } + + /** + * Check that setting UserId null on the message causes any existing value to be cleared + * + * @throws Exception if an error occurs during the test. + */ + @Test + public void testSetUserIdBytesNullOnMessageWithExistingUserId() throws Exception { + String userIdString = "testValue"; + byte[] bytes = userIdString.getBytes("UTF-8"); + + AmqpJmsMessageFacade amqpMessageFacade = createNewMessageFacade(); + + amqpMessageFacade.setUserIdBytes(bytes); + amqpMessageFacade.setUserId(null); + + assertNotNull("properties section was not created", amqpMessageFacade.getAmqpMessage().getProperties()); + assertNull("bytes were not cleared as expected for userid", amqpMessageFacade.getAmqpMessage().getProperties().getUserId()); + assertNull("userid bytes not as expected", amqpMessageFacade.getUserIdBytes()); + } + + @Test + public void testClearUserIdBytesWithNoExistingProperties() { + AmqpJmsMessageFacade amqpMessageFacade = createNewMessageFacade(); + + amqpMessageFacade.setUserIdBytes(null); + + assertNull("underlying message should still have no properties setion", amqpMessageFacade.getAmqpMessage().getProperties()); + assertEquals("UserId should be null", null, amqpMessageFacade.getUserIdBytes()); + } + // ====== AMQP Message Annotations ======= // ======================================= --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
