diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java index 8a892f6598d..dfbc499c081 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java @@ -1538,4 +1538,6 @@ default String resolvePropertiesSources(String propertiesFileUrl) { default boolean isUsingDatabasePersistence() { return getStoreConfiguration() != null && getStoreConfiguration().getStoreType() == StoreConfiguration.StoreType.DATABASE; } + + Map getJaasConfigs(); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/JaasAppConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/JaasAppConfiguration.java new file mode 100644 index 00000000000..73ad330389d --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/JaasAppConfiguration.java @@ -0,0 +1,78 @@ +/* + * 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.core.config; + +import javax.security.auth.login.AppConfigurationEntry; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class JaasAppConfiguration implements Serializable { + + private static final long serialVersionUID = -651209063030767325L; + + private String name; + + private List modules = new ArrayList<>(); + + public JaasAppConfiguration() { + } + + public String getName() { + return name; + } + + public JaasAppConfiguration setName(String name) { + this.name = name; + return this; + } + + public List getModules() { + return modules; + } + + // help the properties setter + public JaasAppConfiguration addModule(JaasAppConfigurationEntry entry) { + modules.add(entry); + return this; + } + + public static AppConfigurationEntry[] asAppConfigurationEntry(JaasAppConfiguration jaasAppConfiguration) { + if (jaasAppConfiguration == null) { + return null; + } + AppConfigurationEntry[] entries = new AppConfigurationEntry[jaasAppConfiguration.getModules().size()]; + for (int i = 0; i < jaasAppConfiguration.getModules().size(); i++) { + JaasAppConfigurationEntry jaasAppConfigurationEntry = jaasAppConfiguration.getModules().get(i); + entries[i] = new AppConfigurationEntry(jaasAppConfigurationEntry.getLoginModuleClass(), jaasAppConfigurationEntry.getLoginModuleControlFlag(), jaasAppConfigurationEntry.getParams()); + } + return entries; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + JaasAppConfiguration that = (JaasAppConfiguration) o; + return Objects.equals(name, that.name) && Objects.equals(modules, that.modules); + } + + @Override + public int hashCode() { + return Objects.hash(name, modules); + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationEntry.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationEntry.java new file mode 100644 index 00000000000..934ec57eeef --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationEntry.java @@ -0,0 +1,100 @@ +/* + * 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.core.config; + +import javax.security.auth.login.AppConfigurationEntry; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class JaasAppConfigurationEntry implements Serializable { + + private static final long serialVersionUID = -651209063030767725L; + + private String name; + + private String loginModuleClass; + + private String controlFlag; + + private Map params = new HashMap<>(); + + public JaasAppConfigurationEntry() { + } + + public String getName() { + return name; + } + + public String getLoginModuleClass() { + return loginModuleClass; + } + + public Map getParams() { + return params; + } + + public String getControlFlag() { + return controlFlag; + } + + public JaasAppConfigurationEntry setName(String name) { + this.name = name; + return this; + } + + public JaasAppConfigurationEntry setLoginModuleClass(String loginModuleClass) { + this.loginModuleClass = loginModuleClass; + return this; + } + + public JaasAppConfigurationEntry setParams(Map params) { + this.params = params; + return this; + } + + public void setControlFlag(String controlFlag) { + this.controlFlag = controlFlag; + getLoginModuleControlFlag(); + } + + AppConfigurationEntry.LoginModuleControlFlag getLoginModuleControlFlag() { + if (this.controlFlag == null || this.controlFlag.isEmpty() || this.controlFlag.equals("required")) { + return AppConfigurationEntry.LoginModuleControlFlag.REQUIRED; + } else if (this.controlFlag.equals("requisite")) { + return AppConfigurationEntry.LoginModuleControlFlag.REQUISITE; + } else if (this.controlFlag.equals("optional")) { + return AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL; + } else if (this.controlFlag.equals("sufficient")) { + return AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT; + } + throw new IllegalArgumentException("Unknown control flag: " + this.controlFlag); + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + JaasAppConfigurationEntry that = (JaasAppConfigurationEntry) o; + return Objects.equals(name, that.name) && Objects.equals(loginModuleClass, that.loginModuleClass) && Objects.equals(controlFlag, that.controlFlag) && Objects.equals(params, that.params); + } + + @Override + public int hashCode() { + return Objects.hash(name, loginModuleClass, controlFlag, params); + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java index a81dda1a4d4..f8a71e017f0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java @@ -57,6 +57,7 @@ import java.util.Properties; import java.util.Set; import java.util.Stack; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -86,6 +87,7 @@ import org.apache.activemq.artemis.core.config.DivertConfiguration; import org.apache.activemq.artemis.core.config.FederationConfiguration; import org.apache.activemq.artemis.core.config.HAPolicyConfiguration; +import org.apache.activemq.artemis.core.config.JaasAppConfiguration; import org.apache.activemq.artemis.core.config.MetricsConfiguration; import org.apache.activemq.artemis.core.config.StoreConfiguration; import org.apache.activemq.artemis.core.config.WildcardConfiguration; @@ -159,9 +161,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.security.auth.login.AppConfigurationEntry; + import static org.apache.activemq.artemis.utils.PasswordMaskingUtil.isEncMasked; -public class ConfigurationImpl implements Configuration, Serializable { +public class ConfigurationImpl extends javax.security.auth.login.Configuration implements Configuration, Serializable { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @@ -478,6 +482,8 @@ public class ConfigurationImpl implements Configuration, Serializable { private boolean purgePageFolders = ActiveMQDefaultConfiguration.getPurgePageFolders(); + private Map jaasConfigs = new ConcurrentHashMap<>(); + /** * Parent folder for all data folders. */ @@ -671,6 +677,26 @@ public void parsePrefixedProperties(Object target, String name, Properties prope if (!beanProperties.isEmpty()) { populateWithProperties(target, name, beanProperties); } + if (!jaasConfigs.isEmpty()) { + initJaasConfigOverride(); + } + } + + private javax.security.auth.login.Configuration defaultJaasConfiguration = null; + private void initJaasConfigOverride() { + if (defaultJaasConfiguration == null) { + defaultJaasConfiguration = javax.security.auth.login.Configuration.getConfiguration(); + javax.security.auth.login.Configuration.setConfiguration(this); + } + } + + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String realm) { + if (getJaasConfigs().containsKey(realm)) { + return JaasAppConfiguration.asAppConfigurationEntry(getJaasConfigs().get(realm)); + } else { + return defaultJaasConfiguration.getAppConfigurationEntry(realm); + } } public void populateWithProperties(final Object target, final String propsId, Map beanProperties) throws InvocationTargetException, IllegalAccessException { @@ -1002,6 +1028,15 @@ public void exportAsProperties(File file) throws Exception { } } + @Override + public Map getJaasConfigs() { + return jaasConfigs; + } + + public void addJaasConfig(JaasAppConfiguration config) { + jaasConfigs.put(config.getName(), config); + } + private void writeProperties(FileWriter writer) throws Exception { final BeanUtilsBean beanUtilsBean = new BeanUtilsBean(); beanUtilsBean.getPropertyUtils().addBeanIntrospector(new FluentPropertyBeanIntrospectorWithIgnores()); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java index 0c4fa77bcb6..da2f59d930e 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java @@ -41,8 +41,8 @@ public class GuestLoginModule implements AuditLoginModule { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private static final String GUEST_USER = "org.apache.activemq.jaas.guest.user"; - private static final String GUEST_ROLE = "org.apache.activemq.jaas.guest.role"; + public static final String GUEST_USER = "org.apache.activemq.jaas.guest.user"; + public static final String GUEST_ROLE = "org.apache.activemq.jaas.guest.role"; private String userName = "guest"; private String roleName = "guests"; diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationEntryTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationEntryTest.java new file mode 100644 index 00000000000..a0d14d2af63 --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationEntryTest.java @@ -0,0 +1,121 @@ +/* + * 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.core.config; + +import org.junit.jupiter.api.Test; + +import javax.security.auth.login.AppConfigurationEntry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class JaasAppConfigurationEntryTest { + + @Test + void testEquals() { + JaasAppConfigurationEntry a = new JaasAppConfigurationEntry(); + JaasAppConfigurationEntry b = new JaasAppConfigurationEntry(); + + assertTrue(a.equals(b)); + + a.setName("test"); + assertFalse(a.equals(b)); + + b.setName("test"); + assertTrue(a.equals(b)); + + a.setControlFlag("optional"); + assertFalse(a.equals(b)); + + b.setControlFlag("optional"); + assertTrue(a.equals(b)); + + a.setLoginModuleClass("module"); + assertFalse(a.equals(b)); + + b.setLoginModuleClass("module"); + assertTrue(a.equals(b)); + + a.getParams().put("key1", "value1"); + assertFalse(a.equals(b)); + + b.getParams().put("key1", "value1"); + assertTrue(a.equals(b)); + + a.getParams().put("key2", "value1"); + assertFalse(a.equals(b)); + b.getParams().put("key2", "value2"); + assertFalse(a.equals(b)); + } + + @Test + void testHashCode() { + + JaasAppConfigurationEntry a = new JaasAppConfigurationEntry(); + JaasAppConfigurationEntry b = new JaasAppConfigurationEntry(); + assertEquals(a.hashCode(), b.hashCode()); + + a.setName("test"); + assertNotEquals(a.hashCode(), b.hashCode()); + + b.setName("test"); + assertEquals(a.hashCode(), b.hashCode()); + + a.setControlFlag("optional"); + assertNotEquals(a.hashCode(), b.hashCode()); + + b.setControlFlag("optional"); + assertEquals(a.hashCode(), b.hashCode()); + + a.setLoginModuleClass("module"); + assertNotEquals(a.hashCode(), b.hashCode()); + + b.setLoginModuleClass("module"); + assertEquals(a.hashCode(), b.hashCode()); + + a.getParams().put("key1", "value1"); + assertNotEquals(a.hashCode(), b.hashCode()); + + b.getParams().put("key1", "value1"); + assertEquals(a.hashCode(), b.hashCode()); + + } + + @Test + void setControlFlag() { + assertThrowsExactly(IllegalArgumentException.class, () -> new JaasAppConfigurationEntry().setControlFlag("null")); + + JaasAppConfigurationEntry a = new JaasAppConfigurationEntry(); + a.setControlFlag(null); + assertEquals(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, a.getLoginModuleControlFlag()); + + a.setControlFlag("required"); + assertEquals(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, a.getLoginModuleControlFlag()); + + a.setControlFlag("optional"); + assertEquals(AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL, a.getLoginModuleControlFlag()); + + a.setControlFlag("requisite"); + assertEquals(AppConfigurationEntry.LoginModuleControlFlag.REQUISITE, a.getLoginModuleControlFlag()); + + a.setControlFlag("sufficient"); + assertEquals(AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT, a.getLoginModuleControlFlag()); + } +} \ No newline at end of file diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationTest.java new file mode 100644 index 00000000000..fc38b81979e --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/JaasAppConfigurationTest.java @@ -0,0 +1,61 @@ +/* + * 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.core.config; + +import org.junit.jupiter.api.Test; + +import javax.security.auth.login.AppConfigurationEntry; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class JaasAppConfigurationTest { + + @Test + void asAppConfigurationEntry() { + JaasAppConfiguration underTest = new JaasAppConfiguration(); + underTest.setName("test"); + JaasAppConfigurationEntry entry = new JaasAppConfigurationEntry(); + entry.setName("test"); + entry.setLoginModuleClass("a"); + + underTest.addModule(entry); + underTest.addModule(entry); + + assertEquals(2, JaasAppConfiguration.asAppConfigurationEntry(underTest).length); + assertEquals(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, JaasAppConfiguration.asAppConfigurationEntry(underTest)[0].getControlFlag()); + } + + @Test + void testEquals() { + JaasAppConfiguration a = new JaasAppConfiguration(); + JaasAppConfiguration b = new JaasAppConfiguration(); + assertEquals(a, b); + + a.setName(this.getClass().getName()); + assertNotEquals(a, b); + b.setName(this.getClass().getName()); + assertEquals(a, b); + JaasAppConfigurationEntry entry = new JaasAppConfigurationEntry(); + entry.setName(this.getClass().getName()); + a.getModules().add(entry); + assertNotEquals(a, b); + b.getModules().add(entry); + assertEquals(a, b); + } +} \ No newline at end of file diff --git a/docs/user-manual/configuration-index.adoc b/docs/user-manual/configuration-index.adoc index e90943b62ac..db0836fa090 100644 --- a/docs/user-manual/configuration-index.adoc +++ b/docs/user-manual/configuration-index.adoc @@ -115,6 +115,15 @@ acceptorConfigurations.tcp.params.port=61616 . set the acceptor named "tcp" 'HOST' parameter to localhost . set the acceptor named "tcp" 'PORT' parameter to 61616 +=== JAAS +Broker properties can configure JAAS via the `jaasConfigs` collection, this provides a means to dynamically configure the jaas realms accessed by the broker. The jaas configuration provider will look first for any realm configured in the `jaasConfigs` collection before delegating to the platform defaults. +For example, to configure the `guestRealm` and use it from the above `tcp` acceptor, configure properties of the form: + +---- +acceptorConfigurations.tcp.params.securityDomain=guestRealm +jaasConfigs.guestRealm.modules.guest.loginModuleClass=org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule +jaasConfigs.guestRealm.modules.guest.controlFlag=required +---- ==== Usage The `artemis run` command script supports `--properties ` where properties file paths can be configured. diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorJmsTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorJmsTest.java index 49e09024b5b..271055deb8b 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorJmsTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorJmsTest.java @@ -16,41 +16,56 @@ */ package org.apache.activemq.artemis.tests.integration.security; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - import javax.jms.Connection; import javax.jms.ConnectionFactory; import javax.jms.JMSException; +import javax.jms.JMSSecurityException; import javax.jms.MessageConsumer; import javax.jms.MessageProducer; import javax.jms.QueueBrowser; import javax.jms.Session; +import javax.security.auth.Subject; import java.lang.management.ManagementFactory; import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import java.util.Stack; +import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.api.core.QueueConfiguration; import org.apache.activemq.artemis.api.core.RoutingType; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl; +import org.apache.activemq.artemis.core.persistence.OperationContext; import org.apache.activemq.artemis.core.security.Role; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.core.server.ActiveMQServers; import org.apache.activemq.artemis.core.server.impl.AddressInfo; +import org.apache.activemq.artemis.core.server.plugin.ActiveMQServerSessionPlugin; import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory; +import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.apache.activemq.artemis.spi.core.protocol.SessionCallback; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; +import org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule; +import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal; import org.apache.activemq.artemis.tests.extensions.parameterized.Parameter; import org.apache.activemq.artemis.tests.extensions.parameterized.ParameterizedTestExtension; import org.apache.activemq.artemis.tests.extensions.parameterized.Parameters; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.qpid.jms.JmsConnectionFactory; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + @ExtendWith(ParameterizedTestExtension.class) public class SecurityPerAcceptorJmsTest extends ActiveMQTestBase { @@ -68,7 +83,7 @@ public static Collection parameters() { } @Parameter(index = 0) - public Protocol protocol; + public Protocol protocol = Protocol.AMQP; static { String path = System.getProperty("java.security.auth.login.config"); @@ -93,11 +108,70 @@ public void setUp() throws Exception { cf = new ActiveMQConnectionFactory(URL); break; case OPENWIRE: - cf = new org.apache.activemq.ActiveMQConnectionFactory(URL); + cf = new org.apache.activemq.ActiveMQConnectionFactory(URL + "?jms.watchTopicAdvisories=false"); break; case AMQP: cf = new JmsConnectionFactory("amqp://localhost:61616"); + } + } + @TestTemplate + public void testJAASSecurityManagerRealmFromPropertiesConfigOk() throws Exception { + ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true).setResolveProtocols(true).addAcceptorConfiguration("netty", URL), ManagementFactory.getPlatformMBeanServer(), new ActiveMQJAASSecurityManager(), false)); + + final String CUSTOM_GUEST_USER = "foo"; + ConfigurationImpl.InsertionOrderedProperties props = new ConfigurationImpl.InsertionOrderedProperties(); + props.put("acceptorConfigurations.netty.params.securityDomain", "first"); + props.put("jaasConfigs.first.modules.guest.loginModuleClass", "org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule"); + props.put("jaasConfigs.first.modules.guest.controlFlag", "required"); + props.put("jaasConfigs.first.modules.guest.params.debug", "true"); + // these need surround because the login module param keys have dots + props.put("jaasConfigs.first.modules.guest.params.\"" + GuestLoginModule.GUEST_USER + "\"", CUSTOM_GUEST_USER); + + server.getConfiguration().parsePrefixedProperties(server.getConfiguration(), "test", props, ""); + assertEquals(2, server.getConfiguration().getStatus().split(":\\[\\]").length); + + final Stack subjects = new Stack<>(); + server.getBrokerSessionPlugins().add(new ActiveMQServerSessionPlugin() { + @Override + public void beforeCreateSession(String name, String username, int minLargeMessageSize, RemotingConnection connection, boolean autoCommitSends, boolean autoCommitAcks, boolean preAcknowledge, boolean xa, String defaultAddress, SessionCallback callback, boolean autoCreateQueues, OperationContext context, Map prefixes) throws ActiveMQException { + subjects.add(connection.getSubject()); + } + }); + server.start(); + assertTrue(server.getConfiguration().getJaasConfigs().get("first").getModules().get(0).getParams().containsKey(GuestLoginModule.GUEST_USER)); + try (Connection c = cf.createConnection("first", "secret")) { + try (Session s = c.createSession(false, 1)) { + Thread.sleep(200); + } + } catch (JMSException e) { + fail("should not throw exception but " + e); + } + + Assertions.assertFalse(subjects.empty()); + Subject subject = subjects.pop(); + assertTrue(subject.getPrincipals(UserPrincipal.class).stream().findFirst().isPresent()); + assertTrue(subject.getPrincipals(UserPrincipal.class).stream().findFirst().get().getName().equals(CUSTOM_GUEST_USER)); + } + + @Test + public void testJAASSecurityManagerRealmFromIncorrectConfig() throws Exception { + ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true).setResolveProtocols(true).addAcceptorConfiguration("netty", URL + "?securityDomain=first"), ManagementFactory.getPlatformMBeanServer(), new ActiveMQJAASSecurityManager(), false)); + + ConfigurationImpl.InsertionOrderedProperties props = new ConfigurationImpl.InsertionOrderedProperties(); + // no login module class configured + props.put("jaasConfigs.first.modules.guest.controlFlag", "required"); + + server.getConfiguration().parsePrefixedProperties(server.getConfiguration(), "test", props, ""); + assertEquals(2, server.getConfiguration().getStatus().split(":\\[\\]").length); + + server.start(); + try (Connection c = cf.createConnection("first", "secret")) { + try (Session s = c.createSession(false, 1)) { + // no op + } + fail("should throw exception here"); + } catch (JMSSecurityException expected) { } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ConfigurationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ConfigurationTest.java index 7f4dd0c7e37..329f2cf5066 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ConfigurationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ConfigurationTest.java @@ -107,6 +107,7 @@ public void testPropertiesConfigReload() throws Exception { config.put("addressConfigurations.mytopic_4.queueConfigs.\"queue.B4\".routingType", "MULTICAST"); config.put("status", "{\"generation\": \"1\"}"); + config.put("jaasConfigs.artemisRealm.modules.guest.controlFlag", "optional"); try (FileOutputStream outStream = new FileOutputStream(propsFile)) { config.store(outStream, null); @@ -127,6 +128,9 @@ public void testPropertiesConfigReload() throws Exception { Bindings mytopic_3 = server.getPostOffice().getBindingsForAddress(SimpleString.of("mytopic_3")); assertEquals(2, mytopic_3.getBindings().size()); + assertEquals(1, server.getConfiguration().getJaasConfigs().size()); + config.remove("jaasConfigs.artemisRealm.modules.guest.controlFlag"); + config.put("jaasConfigs.artemisRealm", "-"); Bindings myqueue_1 = server.getPostOffice().getBindingsForAddress(SimpleString.of("myqueue_1")); assertEquals(1, myqueue_1.getBindings().size()); @@ -148,6 +152,8 @@ public void testPropertiesConfigReload() throws Exception { Bindings mytopic_31 = server.getPostOffice().getBindingsForAddress(SimpleString.of("mytopic_3")); return mytopic_31.getBindings().size() == 3; }); + // verify empty errors in the status json + assertEquals(3, server.getConfiguration().getStatus().split(":\\[\\]").length, server.getConfiguration().getStatus()); // verify round trip apply assertTrue(server.getActiveMQServerControl().getStatus().contains("2")); @@ -156,6 +162,8 @@ public void testPropertiesConfigReload() throws Exception { assertTrue(server.getActiveMQServerControl().getStatus().contains("version")); assertTrue(server.getActiveMQServerControl().getStatus().contains("uptime")); + assertEquals(0, server.getConfiguration().getJaasConfigs().size()); + } finally { try { server.stop();