[ 
https://issues.apache.org/jira/browse/ARTEMIS-4963?focusedWorklogId=928539&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-928539
 ]

ASF GitHub Bot logged work on ARTEMIS-4963:
-------------------------------------------

                Author: ASF GitHub Bot
            Created on: 02/Aug/24 14:54
            Start Date: 02/Aug/24 14:54
    Worklog Time Spent: 10m 
      Work Description: tabish121 commented on code in PR #5122:
URL: https://github.com/apache/activemq-artemis/pull/5122#discussion_r1701963274


##########
tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SecurityOpenWireTest.java:
##########
@@ -16,70 +16,675 @@
  */
 package org.apache.activemq.artemis.tests.integration.openwire;
 
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Set;
+
 import javax.jms.Connection;
 import javax.jms.DeliveryMode;
-import javax.jms.JMSException;
+import javax.jms.Destination;
+import javax.jms.JMSSecurityException;
 import javax.jms.MessageProducer;
-import javax.jms.Queue;
 import javax.jms.Session;
-import java.util.HashSet;
-import java.util.Set;
-
 import org.apache.activemq.artemis.api.core.QueueConfiguration;
 import org.apache.activemq.artemis.api.core.RoutingType;
 import org.apache.activemq.artemis.core.config.Configuration;
 import org.apache.activemq.artemis.core.security.Role;
 import 
org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
+import org.apache.activemq.artemis.tests.util.RandomUtil;
+import org.apache.activemq.artemis.utils.CompositeAddress;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 
-import static org.junit.jupiter.api.Assertions.fail;
-
+@Timeout(20)
 public class SecurityOpenWireTest extends BasicOpenWireTest {
 
+   // This set specifically tests that two FQQNs under the same address can be 
isolated for
+   // two distinct users each being allowed only access to a given queue under 
the address
+   private final String FQQN_USER1 = "fqqnUser1";
+   private final String FQQN_USER2 = "fqqnUser2";
+   private final String FQQN_ROLE1 = "fqqnRole1";
+   private final String FQQN_ROLE2 = "fqqnRole2";
+   private final String FQQN_ADDRESS = "fqqnAddress";
+   private final String FQQN_QUEUE1 = "fqqnQueue1";
+   private final String FQQN_QUEUE2 = "fqqnQueue2";
+   private final String FQQN_FOR_USER1 = 
CompositeAddress.toFullyQualified(FQQN_ADDRESS, FQQN_QUEUE1);
+   private final String FQQN_FOR_USER2 = 
CompositeAddress.toFullyQualified(FQQN_ADDRESS, FQQN_QUEUE2);
+
+   private final String ALLOWED_USER = "allowedUser";
+   private final String ALLOWED_ROLE = "allowedRole";
+   private final String ALLOWED_USER_ALTERNATE = "allowedUserAlternate";
+   private final String ALLOWED_ROLE_ALTERNATE = "allowedRoleAlternate";
+   private final String DENIED_USER = "deniedUser";
+   private final String DENIED_ROLE = "deniedRole";
+   private final String PASS = RandomUtil.randomString();
+   private final String ADDRESS = "myAddress";
+   private final String ALTERNATE_ADDRESS = "myOtherAddress";
+   private final String ALTERNATE_QUEUE = "myOtherQueue";
+   private final String QUEUE = "myQueue";
+   private final String FQQN = CompositeAddress.toFullyQualified(ADDRESS, 
QUEUE);
+   private final String FQQN_ALTERNATE = 
CompositeAddress.toFullyQualified(ALTERNATE_ADDRESS, ALTERNATE_QUEUE);
+
    @Override
    @BeforeEach
    public void setUp() throws Exception {
-      //this system property is used to construct the executor in
-      
//org.apache.activemq.transport.AbstractInactivityMonitor.createExecutor()
-      //and affects the pool's shutdown time. (default is 30 sec)
-      //set it to 2 to make tests shutdown quicker.
+      // this system property is used to construct the executor in
+      // 
org.apache.activemq.transport.AbstractInactivityMonitor.createExecutor()
+      // and affects the pool's shutdown time. (default is 30 sec)
+      // set it to 2 to make tests shutdown quicker.
       
System.setProperty("org.apache.activemq.transport.AbstractInactivityMonitor.keepAliveTime",
 "2");
-      this.realStore = true;
+
+      realStore = true;
       enableSecurity = true;
+
       super.setUp();
    }
 
    @Override
-   protected void extraServerConfig(Configuration serverConfig) {
+   protected void extraServerConfig(Configuration configuration) {
+      super.extraServerConfig(configuration);
+
+      final Role allowed = new Role(ALLOWED_ROLE, true, false, false, false, 
false, false, false, false, true, false, false, false);
+      final Role denied = new Role(DENIED_ROLE, false, false, false, false, 
false, false, false, false, false, false, false, false);
+      final Role alternate = new Role(ALLOWED_ROLE_ALTERNATE, true, false, 
false, false, false, false, false, false, true, false, false, false);
+
+      final ActiveMQJAASSecurityManager securityManager = 
(ActiveMQJAASSecurityManager) server.getSecurityManager();
+
+      securityManager.getConfiguration().addUser(ALLOWED_USER, PASS);
+      securityManager.getConfiguration().addRole(ALLOWED_USER, ALLOWED_ROLE);
+      securityManager.getConfiguration().addUser(ALLOWED_USER_ALTERNATE, PASS);
+      securityManager.getConfiguration().addRole(ALLOWED_USER_ALTERNATE, 
ALLOWED_ROLE_ALTERNATE);
+      securityManager.getConfiguration().addUser(DENIED_USER, PASS);
+      securityManager.getConfiguration().addRole(DENIED_USER, DENIED_ROLE);
+      securityManager.getConfiguration().addRole(ALLOWED_USER, 
"advisoryReceiver");
+      securityManager.getConfiguration().addRole(ALLOWED_USER_ALTERNATE, 
"advisoryReceiver");
+      securityManager.getConfiguration().addRole(DENIED_USER, 
"advisoryReceiver");
+
+      configuration.putSecurityRoles(FQQN, Set.of(allowed, denied));
+      configuration.putSecurityRoles(ADDRESS, Set.of(allowed, denied));
+      configuration.putSecurityRoles(FQQN_ALTERNATE, Set.of(alternate, 
denied));
+      configuration.putSecurityRoles(ALTERNATE_ADDRESS, Set.of(alternate, 
denied));
+
+      
configuration.addQueueConfiguration(QueueConfiguration.of(QUEUE).setAddress(ADDRESS).setRoutingType(RoutingType.ANYCAST));
+      
configuration.addQueueConfiguration(QueueConfiguration.of(ALTERNATE_QUEUE).setAddress(ALTERNATE_ADDRESS).setRoutingType(RoutingType.ANYCAST));
+
+      // This section create a split FQQN set under a single address where 
each user can only write to their
+      // own Queue under the base FQQN address, these roles disallow auto 
create to ensure neither can just
+      // create their way into a working configuration.
+
+      final Role fqqn1 = new Role(FQQN_ROLE1, true, false, false, false, 
false, false, false, false, false, false, false, false);
+      final Role fqqn2 = new Role(FQQN_ROLE2, true, false, false, false, 
false, false, false, false, false, false, false, false);
+
+      securityManager.getConfiguration().addUser(FQQN_USER1, PASS);
+      securityManager.getConfiguration().addRole(FQQN_USER1, FQQN_ROLE1);
+      securityManager.getConfiguration().addRole(FQQN_USER1, 
"advisoryReceiver");
+      securityManager.getConfiguration().addUser(FQQN_USER2, PASS);
+      securityManager.getConfiguration().addRole(FQQN_USER2, FQQN_ROLE2);
+      securityManager.getConfiguration().addRole(FQQN_USER2, 
"advisoryReceiver");
+
+      configuration.putSecurityRoles(FQQN_FOR_USER1, Set.of(fqqn1));
+      configuration.putSecurityRoles(FQQN_FOR_USER2, Set.of(fqqn2));
+
+      
configuration.addQueueConfiguration(QueueConfiguration.of(FQQN_FOR_USER1).setAddress(FQQN_ADDRESS).setRoutingType(RoutingType.ANYCAST));
+      
configuration.addQueueConfiguration(QueueConfiguration.of(FQQN_FOR_USER2).setAddress(FQQN_ADDRESS).setRoutingType(RoutingType.ANYCAST));
+   }
+
+   @Test
+   public void testAnonymousSendNoAuthToQueue() throws Exception {
+      doTestAnonymousSendNoAuth(false);
+   }
+
+   @Test
+   public void testAnonymousSendNoAuthToTopic() throws Exception {
+      doTestAnonymousSendNoAuth(true);
+   }
+
+   private void doTestAnonymousSendNoAuth(boolean topic) throws Exception {
+      try (Connection connection = factory.createConnection(DENIED_USER, 
PASS)) {
+         final Session session = connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Destination destination;
+
+         if (topic) {
+            destination = session.createTopic(ADDRESS);
+         } else {
+            destination = session.createQueue(ADDRESS);
+         }
+
+         final MessageProducer producer = session.createProducer(null);
+
+         producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+
+         try {
+            producer.send(destination, session.createTextMessage());
+            fail("Should not be able to send to this destination");
+         } catch (JMSSecurityException e) {
+            // expected to fail as not authorized
+         }
+      }
+   }
+
+   @Test
+   public void testAnonymousSendWithAuthToQueue() throws Exception {
+      doTestAnonymousSendWithAuth(false);
+   }
+
+   @Test
+   public void testAnonymousSendWithAuthToTopic() throws Exception {
+      doTestAnonymousSendWithAuth(true);
+   }
+
+   private void doTestAnonymousSendWithAuth(boolean topic) throws Exception {
+      try (Connection connection = factory.createConnection(ALLOWED_USER, 
PASS)) {
+         final Session session = connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final MessageProducer producer = session.createProducer(null);
+         final Destination destination;
+
+         if (topic) {
+            destination = session.createTopic(ADDRESS);
+         } else {
+            destination = session.createQueue(ADDRESS);
+         }
+
+         producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+
+         try {
+            producer.send(destination, session.createMessage());
+            // Expected: can send to allowed destination
+         } catch (JMSSecurityException e) {
+            fail("unexpected error: " + e.getMessage());
+         }
+      }
+   }
+
+   @Test
+   public void testSendWithAuthToQueue() throws Exception {
+      doTestSendWithAuth(false);
+   }
+
+   @Test
+   public void testSendWithAuthToTopic() throws Exception {
+      doTestSendWithAuth(true);
+   }
+
+   private void doTestSendWithAuth(boolean topic) throws Exception {
+      try (Connection connection = factory.createConnection(ALLOWED_USER, 
PASS)) {
+         final Session session = connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Destination destination;
+
+         if (topic) {
+            destination = session.createTopic(ADDRESS);
+         } else {
+            destination = session.createQueue(ADDRESS);
+         }
+
+         final MessageProducer producer = session.createProducer(destination);
+
+         producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+
+         try {
+            producer.send(session.createMessage());
+            // Expected: can send to allowed destination
+         } catch (JMSSecurityException e) {
+            fail("unexpected error: " + e.getMessage());
+         }
+      }
+   }
+
+   @Test
+   public void testCreateSenderNoAuthToQueue() throws Exception {
+      doTestCreateSenderNoAuth(false);
+   }
+
+   @Test
+   public void testCreateSenderNoAuthToTopic() throws Exception {
+      doTestCreateSenderNoAuth(true);
+   }
+
+   private void doTestCreateSenderNoAuth(boolean topic) throws Exception {
+      try (Connection connection = factory.createConnection(DENIED_USER, 
PASS)) {
+         final Session session = connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Destination destination;
+
+         if (topic) {
+            destination = session.createTopic(ADDRESS);
+         } else {
+            destination = session.createQueue(ADDRESS);
+         }
+
+         try {
+            session.createProducer(destination);
+            fail("Should not be able to create producer on restricted 
destination");
+         } catch (JMSSecurityException e) {
+            // expected to fail as not authorized
+         }
+      }
+   }
+
+   @Test
+   public void testCreateSenderWithAuthToFQQNAsQueue() throws Exception {
+      doTestCreateSenderWithAuthToFQQN(false);
+   }
+
+   @Test
+   public void testCreateSenderWithAuthToFQQNAsTopic() throws Exception {
+      doTestCreateSenderWithAuthToFQQN(true);
+   }
+
+   private void doTestCreateSenderWithAuthToFQQN(boolean topic) throws 
Exception {
+      try (Connection connection = factory.createConnection(ALLOWED_USER, 
PASS)) {
+         final Session session = connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Destination destination;
+         final Destination otherDestination1;
+
+         if (topic) {
+            destination = session.createTopic(FQQN);
+            otherDestination1 = session.createTopic(FQQN_ALTERNATE); // Does 
not have authorization to this FQQN
+         } else {
+            destination = session.createQueue(FQQN);
+            otherDestination1 = session.createTopic(FQQN_ALTERNATE); // Does 
not have authorization to this FQQN
+         }
+
+         try {
+            session.createProducer(destination);
+            // Expected: can attach to allowed destination
+         } catch (JMSSecurityException e) {
+            fail("Should be able to send to given FQQN: " + e.getMessage());
+         }
+
+         try {
+            session.createProducer(otherDestination1);
+            fail("Should not be able to send to given alternate FQQN:");
+         } catch (JMSSecurityException e) {
+            // Expected: cannot attach to FQQN destination that is not in the 
security match settings
+         }
+      }
+   }
+
+   @Test
+   public void testCreateSenderNoAuthToFQQNAsQueue() throws Exception {
+      doTestCreateSenderNoAuthToFQQN(false);
+   }
+
+   @Test
+   public void testCreateSenderNoAuthToFQQNAsTopic() throws Exception {
+      doTestCreateSenderNoAuthToFQQN(true);
+   }
+
+   private void doTestCreateSenderNoAuthToFQQN(boolean topic) throws Exception 
{
+      try (Connection connection = factory.createConnection(DENIED_USER, 
PASS)) {
+         final Session session = connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Destination destination;
+
+         if (topic) {
+            destination = session.createTopic(FQQN);
+         } else {
+            destination = session.createQueue(FQQN);
+         }
+
+         try {
+            session.createProducer(destination);
+            fail("Should not be able to send to given FQQN");
+         } catch (JMSSecurityException e) {
+            // Expected: cannot attach to denied destination
+         }
+      }
+   }
+
+   @Test
+   public void testAnonymousSenderWithAuthToFQQNAsQueue() throws Exception {
+      doTestAnonymousSenderWithAuthToFQQN(false);
+   }
+
+   @Test
+   public void testAnonymousSenderWithAuthToFQQNAsTopic() throws Exception {
+      doTestAnonymousSenderWithAuthToFQQN(true);
+   }
+
+   private void doTestAnonymousSenderWithAuthToFQQN(boolean topic) throws 
Exception {
+      try (Connection connection = factory.createConnection(ALLOWED_USER, 
PASS)) {
+         final Session session = connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Destination destination;
+         final Destination otherDestination1;
+
+         if (topic) {
+            destination = session.createTopic(FQQN);
+            otherDestination1 = session.createTopic(FQQN_ALTERNATE); // Does 
not have authorization to this FQQN
+         } else {
+            destination = session.createQueue(FQQN);
+            otherDestination1 = session.createTopic(FQQN_ALTERNATE); // Does 
not have authorization to this FQQN
+         }
+
+         final MessageProducer producer = session.createProducer(null);
+
+         producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+
+         try {
+            producer.send(destination, session.createMessage());
+            // Expected: should send to allowed destination
+         } catch (JMSSecurityException e) {
+            fail("Should be able to send to given FQQN: " + e.getMessage());
+         }
+
+         try {
+            producer.send(otherDestination1, session.createMessage());
+            fail("Should not be able to send to given alternate FQQN: ");
+         } catch (JMSSecurityException e) {
+            // Expected: should fail to send to this destination
+         }
+      }
+   }
+
+   @Test
+   public void testAnonymousSenderNoAuthToFQQNAsQueue() throws Exception {
+      doTestAnonymousSenderNoAuthToFQQN(false);
+   }
+
+   @Test
+   public void testAnonymousSenderNoAuthToFQQNAsTopic() throws Exception {
+      doTestAnonymousSenderNoAuthToFQQN(true);
+   }
+
+   private void doTestAnonymousSenderNoAuthToFQQN(boolean topic) throws 
Exception {
+      try (Connection connection = factory.createConnection(DENIED_USER, 
PASS)) {
+         final Session session = connection.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Destination destination;
+
+         if (topic) {
+            destination = session.createTopic(FQQN);
+         } else {
+            destination = session.createQueue(FQQN);
+         }
+
+         final MessageProducer producer = session.createProducer(null);
+
+         producer.setDeliveryMode(DeliveryMode.PERSISTENT);
+
+         try {
+            producer.send(destination, session.createMessage());
+            fail("Should not be able to send to given FQQN");
+         } catch (JMSSecurityException e) {
+            // Expected: cannot attach to denied destination
+         }
+      }
+   }
+
+   @Test
+   public void testCreateSendersWithFineGrainedAuthToFQQNAsQueues() throws 
Exception {
+      doTestCreateSenderWithFineGrainedAuthToFQQNs(false);
+   }
+
+   @Test
+   public void testCreateSendersWithFineGrainedAuthToFQQNAsTopics() throws 
Exception {
+      doTestCreateSenderWithFineGrainedAuthToFQQNs(true);
+   }
+
+   private void doTestCreateSenderWithFineGrainedAuthToFQQNs(boolean topic) 
throws Exception {
+      try (Connection connection1 = factory.createConnection(ALLOWED_USER, 
PASS);
+           Connection connection2 = 
factory.createConnection(ALLOWED_USER_ALTERNATE, PASS)) {
+
+         final Session session1 = connection1.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Session session2 = connection2.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+         final Destination destination1;
+         final Destination destination2;
+
+         if (topic) {
+            destination1 = session1.createTopic(FQQN);
+            destination2 = session2.createTopic(FQQN_ALTERNATE);
+         } else {
+            destination1 = session1.createQueue(FQQN);
+            destination2 = session2.createQueue(FQQN_ALTERNATE);
+         }
+
+         try {
+            session1.createProducer(destination1);
+            // Expected: should be able to create sender to allowed destination
+         } catch (JMSSecurityException e) {
+            fail("Should be able to create sender to given FQQN: " + 
e.getMessage());
+         }
+
+         try {
+            session1.createProducer(destination2);
+            fail("Should not be able to create sender to given FQQN: ");
+         } catch (JMSSecurityException e) {
+            // Expected: should fail to create sender to this FQQN destination
+         }
+
+         try {
+            session2.createProducer(destination2);
+            // Expected: should be able to create sender to allowed destination
+         } catch (JMSSecurityException e) {
+            fail("Should be able to create sender to given FQQN: " + 
e.getMessage());
+         }
+
+         try {
+            session2.createProducer(destination1);
+            fail("Should not be able to create sender to given FQQN: ");
+         } catch (JMSSecurityException e) {
+            // Expected: should fail to create sender to this FQQN destination
+         }
+      }
+   }
+
+   @Test
+   public void testAnonymousSendersWithFineGrainedAuthToFQQNAsQueues() throws 
Exception {
+      doTestAnonymousSenderWithFineGrainedAuthToFQQNs(false);
+   }
+
+   @Test
+   public void testAnonymousSendersWithFineGrainedAuthToFQQNAsTopics() throws 
Exception {
+      doTestAnonymousSenderWithFineGrainedAuthToFQQNs(true);
+   }
+
+   private void doTestAnonymousSenderWithFineGrainedAuthToFQQNs(boolean topic) 
throws Exception {
+      try (Connection connection1 = factory.createConnection(ALLOWED_USER, 
PASS);
+           Connection connection2 = 
factory.createConnection(ALLOWED_USER_ALTERNATE, PASS)) {
+
+         final Session session1 = connection1.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+         final Session session2 = connection2.createSession(false, 
Session.AUTO_ACKNOWLEDGE);
+
+         final Destination destination1;
+         final Destination destination2;
+
+         if (topic) {
+            destination1 = session1.createTopic(FQQN);
+            destination2 = session2.createTopic(FQQN_ALTERNATE);
+         } else {
+            destination1 = session1.createQueue(FQQN);
+            destination2 = session2.createQueue(FQQN_ALTERNATE);
+         }
+
+         final MessageProducer producer1 = session1.createProducer(null);
+         final MessageProducer producer2 = session2.createProducer(null);
+
+         producer1.setDeliveryMode(DeliveryMode.PERSISTENT); // ALLOWED USER
+         producer2.setDeliveryMode(DeliveryMode.PERSISTENT); // ALTERNATE 
ALLOWED USER
+

Review Comment:
   To remain consistent with existing tests 





Issue Time Tracking
-------------------

    Worklog Id:     (was: 928539)
    Time Spent: 40m  (was: 0.5h)

> Reject openwire senders that lack SEND permissions on attach
> ------------------------------------------------------------
>
>                 Key: ARTEMIS-4963
>                 URL: https://issues.apache.org/jira/browse/ARTEMIS-4963
>             Project: ActiveMQ Artemis
>          Issue Type: Improvement
>          Components: OpenWire
>    Affects Versions: 2.36.0
>            Reporter: Timothy A. Bish
>            Assignee: Timothy A. Bish
>            Priority: Minor
>          Time Spent: 40m
>  Remaining Estimate: 0h
>
> Currently the Openwire producers are allowed to attach even when the named 
> destination(s) it requests don't offer send permissions to the logged in user 
> (the sends themselves are validated).  The sends from these named or from 
> anonymous producers are checked for permission but only after such things as 
> conversion of the message to Core has happened which leads to unnecessary GC 
> overhead and wasted CPU cycles if the send is going to ultimately be 
> rejected.  
> We should reject Openwire senders on attach (which is what the ActiveMQ 
> 'Classic' broker does) and we should check send permissions prior to 
> unnecessarily converting messages to Core to reduce overhead from anonymous 
> senders that are sending into destinations they cannot write to.  This change 
> doesn't introduce any new security but simply  would respond more quickly and 
> efficiently than the current code would.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information, visit: https://activemq.apache.org/contact


Reply via email to