diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/HierarchicalObjectRepository.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/HierarchicalObjectRepository.java index 03dbe7c3030..be6737f2728 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/HierarchicalObjectRepository.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/settings/impl/HierarchicalObjectRepository.java @@ -37,6 +37,7 @@ import org.apache.activemq.artemis.core.settings.HierarchicalRepository; import org.apache.activemq.artemis.core.settings.HierarchicalRepositoryChangeListener; import org.apache.activemq.artemis.core.settings.Mergeable; +import org.apache.activemq.artemis.utils.CompositeAddress; import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -203,7 +204,7 @@ public void addMatch(final String match, final T value, final boolean immutableM lock.writeLock().lock(); try { // an exact match (i.e. one without wildcards) won't impact any other matches so no need to clear the cache - if (wildcardConfiguration.isWild(modifiedMatch)) { + if (wildcardConfiguration.isWild(modifiedMatch) && !CompositeAddress.isFullyQualified(modifiedMatch)) { clearCache(); } else if (modifiedMatch != null && cache.containsKey(modifiedMatch)) { cache.remove(modifiedMatch); @@ -216,7 +217,7 @@ public void addMatch(final String match, final T value, final boolean immutableM Match match1 = new Match<>(modifiedMatch, value, wildcardConfiguration, literal); if (literal) { literalMatches.put(modifiedMatch, match1); - } else if (wildcardConfiguration.isWild(modifiedMatch)) { + } else if (wildcardConfiguration.isWild(modifiedMatch) && !CompositeAddress.isFullyQualified(modifiedMatch)) { wildcardMatches.put(modifiedMatch, match1); } else { exactMatches.put(modifiedMatch, match1); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/WildcardOnboardSecureTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/WildcardOnboardSecureTest.java new file mode 100644 index 00000000000..f408b786a64 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/WildcardOnboardSecureTest.java @@ -0,0 +1,122 @@ +/* + * 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.activemq.artemis.tests.integration.jms.client; + +import org.apache.activemq.artemis.api.core.QueueConfiguration; +import org.apache.activemq.artemis.api.core.RoutingType; +import org.apache.activemq.artemis.core.config.CoreAddressConfiguration; +import org.apache.activemq.artemis.core.security.Role; +import org.apache.activemq.artemis.core.server.ActiveMQServer; +import org.apache.activemq.artemis.core.settings.HierarchicalRepository; +import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; +import org.apache.activemq.artemis.tests.util.JMSTestBase; +import org.apache.activemq.artemis.utils.CompositeAddress; +import org.junit.jupiter.api.Test; + +import javax.jms.Connection; +import javax.jms.DeliveryMode; +import javax.jms.JMSSecurityException; +import javax.jms.Message; +import javax.jms.MessageConsumer; +import javax.jms.MessageProducer; +import javax.jms.ObjectMessage; +import javax.jms.Session; +import javax.jms.Topic; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class WildcardOnboardSecureTest extends JMSTestBase { + + private final String publishTo = "test.topic.A"; + private final String subscribeToWildcard = "test.topic.*"; + private final String clientId = "id1"; + private final String subscriberName = "sub1"; + + @Test + public void testConsumeFromExistingWildcardWithJustConsumePermissionViaLiteral() throws Exception { + final String userPass = "joe"; + Session sendSession = createSession(null, userPass); + MessageProducer producerA = createProducer(sendSession, publishTo); + + ObjectMessage received; + try (MessageConsumer consumerWC = createConsumer(subscribeToWildcard, userPass)) { + Message message = sendSession.createObjectMessage(1); + producerA.send(message); + received = (ObjectMessage) consumerWC.receive(500); + } + assertNotNull(received); + assertNotNull(received.getObject()); + } + + @Test + public void testConsumeFromExistingWildcardWithNoPermFails() { + assertThrows(JMSSecurityException.class, () -> createConsumer(subscribeToWildcard, "deny")); + } + + @Override + protected boolean useSecurity() { + return true; + } + + @Override + protected void extraServerConfig(ActiveMQServer server) { + HierarchicalRepository> securityRepository = server.getSecurityRepository(); + ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); + securityManager.getConfiguration().addUser("deny", "deny"); + securityManager.getConfiguration().addUser("joe", "joe"); + securityManager.getConfiguration().addRole("joe", "joe"); + Role joe = new Role("joe", true, true, false, false, false, false, false, false, false, false, false, false); + Set roles = new HashSet<>(); + // no auto create permissions + roles.add(joe); + + securityRepository.addMatch(publishTo, roles); + securityRepository.addMatch(CompositeAddress.toFullyQualified(subscribeToWildcard, clientId + "." + subscriberName), roles); + securityManager.getConfiguration().addRole("joe", "joe"); + // pre create address and consumer queue as only send/consume permissions exist + server.getConfiguration().addAddressConfiguration(new CoreAddressConfiguration().setName(publishTo).addRoutingType(RoutingType.MULTICAST)); + server.getConfiguration().addQueueConfiguration(QueueConfiguration.of(clientId + "." + subscriberName).setAddress(subscribeToWildcard).setRoutingType(RoutingType.MULTICAST)); + } + + private Session createSession(String clientId, String userPass) throws Exception { + Connection connection = cf.createConnection(userPass, userPass); + if (clientId != null) { + connection.setClientID(clientId); + } + connection.start(); + addConnection(connection); + + return connection.createSession(false, Session.AUTO_ACKNOWLEDGE); + } + + private MessageProducer createProducer(Session session, String topicName) throws Exception { + Topic topic = session.createTopic(topicName); + MessageProducer producer = session.createProducer(topic); + producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT); + return producer; + } + + private MessageConsumer createConsumer(String topicName, String userPass) throws Exception { + Session session = createSession(clientId, userPass); + Topic topic = session.createTopic(topicName); + return session.createDurableConsumer(topic, subscriberName); + } +}