From f9910cd033b715c7fe3212fef6a56b814a076533 Mon Sep 17 00:00:00 2001 From: Justin Bertram Date: Fri, 28 Apr 2023 15:22:13 -0500 Subject: [PATCH] ARTEMIS-4267 original exception lost for NoCacheLoginException When skipping the authentication cache details for the original exception are not logged. This commit ensures these details are logged and adopts the ExceptionUtils class from Apache Commons Lang in lieu of the previous custom implementation. --- .../activemq/artemis/utils/ExceptionUtil.java | 33 ------ .../remoting/impl/netty/NettyAcceptor.java | 6 +- .../core/security/impl/SecurityStoreImpl.java | 7 +- .../security/ActiveMQJAASSecurityManager.java | 19 +--- .../security/ActiveMQSecurityManager5.java | 3 +- .../core/security/jaas/LDAPLoginModule.java | 10 +- .../security/jaas/NoCacheLoginException.java | 8 +- .../JAASSecurityManagerClassLoadingTest.java | 104 ++++++++++++++++++ .../jaas/JAASSecurityManagerTest.java | 70 ++---------- .../security/jaas/NoCacheLoginModule.java | 56 ++++++++++ .../src/test/resources/login.config | 6 +- docs/user-manual/en/security.md | 15 ++- .../example/JAASSecurityManagerWrapper.java | 3 +- .../integration/security/SecurityTest.java | 2 +- .../StompWithClientIdValidationTest.java | 3 +- 15 files changed, 218 insertions(+), 127 deletions(-) delete mode 100644 artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ExceptionUtil.java create mode 100644 artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java create mode 100644 artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/NoCacheLoginModule.java diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ExceptionUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ExceptionUtil.java deleted file mode 100644 index e9f5073a31d..00000000000 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/ExceptionUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.utils; - -import java.util.ArrayList; -import java.util.List; - -public class ExceptionUtil { - public static Throwable getRootCause(final Throwable throwable) { - List list = new ArrayList<>(); - Throwable current = throwable; - while (current != null && list.contains(current) == false) { - list.add(current); - current = current.getCause(); - } - return (list.size() < 2 ? throwable : list.get(list.size() - 1)); - } -} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java index a7ca6273111..37cf6cfe9e6 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/remoting/impl/netty/NettyAcceptor.java @@ -94,8 +94,8 @@ import org.apache.activemq.artemis.spi.core.remoting.ssl.SSLContextFactoryProvider; import org.apache.activemq.artemis.utils.ActiveMQThreadFactory; import org.apache.activemq.artemis.utils.ConfigurationHelper; -import org.apache.activemq.artemis.utils.ExceptionUtil; import org.apache.activemq.artemis.utils.collections.TypedProperties; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -479,7 +479,7 @@ public void initChannel(Channel channel) throws Exception { pipeline.addLast("ssl", getSslHandler(channel.alloc(), peerInfo.getA(), peerInfo.getB())); pipeline.addLast("sslHandshakeExceptionHandler", new SslHandshakeExceptionHandler()); } catch (Exception e) { - Throwable rootCause = ExceptionUtil.getRootCause(e); + Throwable rootCause = ExceptionUtils.getRootCause(e); ActiveMQServerLogger.LOGGER.gettingSslHandlerFailed(channel.remoteAddress().toString(), rootCause.getClass().getName() + ": " + rootCause.getMessage()); logger.debug("Getting SSL handler failed", e); @@ -1036,7 +1036,7 @@ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (cause.getMessage() != null && cause.getMessage().startsWith(SSLHandshakeException.class.getName())) { - Throwable rootCause = ExceptionUtil.getRootCause(cause); + Throwable rootCause = ExceptionUtils.getRootCause(cause); String errorMessage = rootCause.getClass().getName() + ": " + rootCause.getMessage(); ActiveMQServerLogger.LOGGER.sslHandshakeFailed(ctx.channel().remoteAddress().toString(), errorMessage); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java index d4301be5cf4..7f770d97118 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java @@ -189,7 +189,7 @@ public String authenticate(final String user, authenticationCache.put(createAuthenticationCacheKey(user, password, connection), new Pair<>(subject != null, subject)); validatedUser = getUserFromSubject(subject); } catch (NoCacheLoginException e) { - logger.debug("Skipping authentication cache due to exception", e); + handleNoCacheLoginException(e); } } else if (securityManager instanceof ActiveMQSecurityManager4) { validatedUser = ((ActiveMQSecurityManager4) securityManager).validateUser(user, password, connection, securityDomain); @@ -413,12 +413,17 @@ private Subject getSubjectForAuthorization(SecurityAuth auth, ActiveMQSecurityMa authenticationCache.put(createAuthenticationCacheKey(auth.getUsername(), auth.getPassword(), auth.getRemotingConnection()), new Pair<>(subject != null, subject)); return subject; } catch (NoCacheLoginException e) { + handleNoCacheLoginException(e); return null; } } return cached.getB(); } + private void handleNoCacheLoginException(NoCacheLoginException e) { + logger.debug("Skipping authentication cache due to exception: {}", e.getMessage()); + } + // public for testing purposes public void invalidateAuthorizationCache() { authorizationCache.invalidateAll(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java index bcfcc923bc3..e73ebaa6afb 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java @@ -19,6 +19,7 @@ import javax.security.auth.Subject; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; +import java.lang.invoke.MethodHandles; import java.util.Set; import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration; @@ -28,11 +29,9 @@ import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler; import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException; import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal; -import org.apache.activemq.artemis.utils.ExceptionUtil; import org.apache.activemq.artemis.utils.SecurityManagerUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.invoke.MethodHandles; import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getCertsFromConnection; @@ -90,11 +89,14 @@ public boolean validateUser(String user, String password) { } @Override - public Subject authenticate(final String user, final String password, RemotingConnection remotingConnection, final String securityDomain) { + public Subject authenticate(final String user, final String password, RemotingConnection remotingConnection, final String securityDomain) throws NoCacheLoginException { try { return getAuthenticatedSubject(user, password, remotingConnection, securityDomain); } catch (LoginException e) { logger.debug("Couldn't validate user", e); + if (e instanceof NoCacheLoginException) { + throw (NoCacheLoginException) e; + } return null; } } @@ -138,16 +140,7 @@ private Subject getAuthenticatedSubject(final String user, } else { lc = new LoginContext(configurationName, null, new JaasCallbackHandler(user, password, remotingConnection), configuration); } - try { - lc.login(); - } catch (LoginException e) { - Throwable rootCause = ExceptionUtil.getRootCause(e); - if (rootCause instanceof NoCacheLoginException) { - throw (NoCacheLoginException) rootCause; - } else { - throw e; - } - } + lc.login(); return lc.getSubject(); } finally { if (thisLoader != currentLoader) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager5.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager5.java index f3871d01af0..2af3459e2d4 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager5.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager5.java @@ -22,6 +22,7 @@ import org.apache.activemq.artemis.core.security.CheckType; import org.apache.activemq.artemis.core.security.Role; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; +import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException; /** * Used to validate whether a user is authorized to connect to the @@ -44,7 +45,7 @@ public interface ActiveMQSecurityManager5 extends ActiveMQSecurityManager { * @param securityDomain the name of the JAAS security domain to use (can be null) * @return the Subject of the authenticated user, else null */ - Subject authenticate(String user, String password, RemotingConnection remotingConnection, String securityDomain); + Subject authenticate(String user, String password, RemotingConnection remotingConnection, String securityDomain) throws NoCacheLoginException; /** * Determine whether the given user has the correct role for the given check type. diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java index 69d9b5a4372..f8dc9692daf 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java @@ -41,6 +41,7 @@ import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.net.URI; import java.net.URISyntaxException; import java.security.Principal; @@ -59,11 +60,10 @@ import java.util.Set; import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; -import org.apache.activemq.artemis.utils.ExceptionUtil; import org.apache.activemq.artemis.utils.PasswordMaskingUtil; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.lang.invoke.MethodHandles; public class LDAPLoginModule implements AuditLoginModule { @@ -262,9 +262,9 @@ public boolean commit() throws LoginException { } private LoginException handleException(LoginException e) { - Throwable t = ExceptionUtil.getRootCause(e); - if (noCacheExceptions.contains(t.getClass().getName())) { - t.initCause(new NoCacheLoginException()); + Throwable rootCause = ExceptionUtils.getRootCause(e); + if (noCacheExceptions.contains(rootCause.getClass().getName())) { + return (NoCacheLoginException) new NoCacheLoginException(rootCause.getClass().getName() + (rootCause.getMessage() == null ? "" : ": " + rootCause.getMessage())).initCause(e); } return e; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/NoCacheLoginException.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/NoCacheLoginException.java index 6db792f69a6..3dea4c69fbd 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/NoCacheLoginException.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/NoCacheLoginException.java @@ -17,11 +17,13 @@ package org.apache.activemq.artemis.spi.core.security.jaas; -public class NoCacheLoginException extends RuntimeException { +import javax.security.auth.login.LoginException; + +public class NoCacheLoginException extends LoginException { public NoCacheLoginException() { super(); } - public NoCacheLoginException(Exception e) { - super(e); + public NoCacheLoginException(String message) { + super(message); } } diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java new file mode 100644 index 00000000000..0482d1d37bb --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java @@ -0,0 +1,104 @@ +/* + * 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.security.jaas; + +import javax.security.auth.Subject; +import java.io.UnsupportedEncodingException; +import java.lang.invoke.MethodHandles; +import java.net.URL; +import java.net.URLClassLoader; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.activemq.artemis.core.security.CheckType; +import org.apache.activemq.artemis.core.security.Role; +import org.apache.activemq.artemis.core.security.impl.SecurityStoreImpl; +import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@RunWith(Parameterized.class) +public class JAASSecurityManagerClassLoadingTest { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + + @Parameterized.Parameters(name = "newLoader=({0})") + public static Collection data() { + return Arrays.asList(new Object[][] {{true}, {false}}); + } + + static { + String path = System.getProperty("java.security.auth.login.config"); + if (path == null) { + URL resource = PropertiesLoginModuleTest.class.getClassLoader().getResource("login.config"); + if (resource != null) { + try { + path = URLDecoder.decode(resource.getFile(), StandardCharsets.UTF_8.name()); + System.setProperty("java.security.auth.login.config", path); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + } + } + + @Parameterized.Parameter + public boolean usingNewLoader; + + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder(); + + @Test + public void testLoginClassloading() throws Exception { + ClassLoader existingLoader = Thread.currentThread().getContextClassLoader(); + logger.debug("loader: {}", existingLoader); + try { + if (usingNewLoader) { + URLClassLoader simulatedLoader = new URLClassLoader(new URL[]{tmpDir.getRoot().toURI().toURL()}, null); + Thread.currentThread().setContextClassLoader(simulatedLoader); + } + ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("PropertiesLogin"); + + Subject result = securityManager.authenticate("first", "secret", null, null); + + assertNotNull(result); + assertEquals("first", SecurityStoreImpl.getUserFromSubject(result)); + + Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true); + Set roles = new HashSet<>(); + roles.add(role); + boolean authorizationResult = securityManager.authorize(result, roles, CheckType.SEND, "someaddress"); + + assertTrue(authorizationResult); + + } finally { + Thread.currentThread().setContextClassLoader(existingLoader); + } + } +} diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerTest.java index c8ded103c96..b37bdc1aa06 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerTest.java @@ -16,43 +16,19 @@ */ package org.apache.activemq.artemis.core.security.jaas; -import javax.security.auth.Subject; - -import org.apache.activemq.artemis.core.security.CheckType; -import org.apache.activemq.artemis.core.security.Role; -import org.apache.activemq.artemis.core.security.impl.SecurityStoreImpl; -import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.lang.invoke.MethodHandles; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - import java.io.UnsupportedEncodingException; import java.net.URL; -import java.net.URLClassLoader; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; + +import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; +import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException; +import org.junit.Test; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -@RunWith(Parameterized.class) public class JAASSecurityManagerTest { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - @Parameterized.Parameters(name = "newLoader=({0})") - public static Collection data() { - return Arrays.asList(new Object[][] {{true}, {false}}); - } static { String path = System.getProperty("java.security.auth.login.config"); @@ -69,38 +45,14 @@ public static Collection data() { } } - @Parameterized.Parameter - public boolean usingNewLoader; - - @Rule - public TemporaryFolder tmpDir = new TemporaryFolder(); - @Test - public void testLoginClassloading() throws Exception { - ClassLoader existingLoader = Thread.currentThread().getContextClassLoader(); - logger.debug("loader: {}", existingLoader); + public void testNoCacheLoginException() { + ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("testNoCacheLoginException"); try { - if (usingNewLoader) { - URLClassLoader simulatedLoader = new URLClassLoader(new URL[]{tmpDir.getRoot().toURI().toURL()}, null); - Thread.currentThread().setContextClassLoader(simulatedLoader); - } - ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("PropertiesLogin"); - - Subject result = securityManager.authenticate("first", "secret", null, null); - - assertNotNull(result); - assertEquals("first", SecurityStoreImpl.getUserFromSubject(result)); - - Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true); - Set roles = new HashSet<>(); - roles.add(role); - boolean authorizationResult = securityManager.authorize(result, roles, CheckType.SEND, "someaddress"); - - assertTrue(authorizationResult); - - } finally { - Thread.currentThread().setContextClassLoader(existingLoader); + securityManager.authenticate(null, null, null, null); + fail(); + } catch (NoCacheLoginException ncle) { + assertEquals(NoCacheLoginModule.MESSAGE, ncle.getMessage()); } } - } diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/NoCacheLoginModule.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/NoCacheLoginModule.java new file mode 100644 index 00000000000..536ee305bcd --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/NoCacheLoginModule.java @@ -0,0 +1,56 @@ +/** + * 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.security.jaas; + +import javax.security.auth.Subject; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; +import java.util.Map; + +import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException; +import org.apache.activemq.artemis.utils.RandomUtil; + +public class NoCacheLoginModule implements LoginModule { + public static final String MESSAGE = RandomUtil.randomString(); + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, Map map, Map map1) { + } + + @Override + public boolean login() throws LoginException { + throw (NoCacheLoginException) new NoCacheLoginException(MESSAGE).initCause(new FailedLoginException()); + } + + @Override + public boolean commit() throws LoginException { + return false; + } + + @Override + public boolean abort() throws LoginException { + return false; + } + + @Override + public boolean logout() throws LoginException { + return false; + } +} diff --git a/artemis-server/src/test/resources/login.config b/artemis-server/src/test/resources/login.config index 106919bf41d..ad1fa34cf2f 100644 --- a/artemis-server/src/test/resources/login.config +++ b/artemis-server/src/test/resources/login.config @@ -237,4 +237,8 @@ HttpServerAuthenticator { org.apache.activemq.artemis.spi.core.security.jaas.KubernetesLoginModule sufficient debug=true org.apache.activemq.jaas.kubernetes.role="cert-roles.properties"; -}; \ No newline at end of file +}; + +testNoCacheLoginException { + org.apache.activemq.artemis.core.security.jaas.NoCacheLoginModule required; +}; diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index 004b6deafcc..df66ea4a257 100644 --- a/docs/user-manual/en/security.md +++ b/docs/user-manual/en/security.md @@ -835,13 +835,18 @@ system. It is implemented by because it has no default value. Example value: `(member={0})`. - `noCacheExceptions` - comma separated list of class names of exceptions which - may thrown during communication with the LDAP server; default is empty. + may be thrown during communication with the LDAP server; default is empty. Typically any failure to authenticate will be stored in the authentication cache so that the underlying security data store (e.g. LDAP) is spared any unnecessary - traffic. However, in cases where the failure is, for example, due to a temporary - network outage and the `security-invalidation-interval` is relatively high this - can be problematic. Users can enumerate any relevant exceptions which the cache - should ignore (e.g. `java.net.ConnectException`) to avoid any such problems. + traffic. For example, an application with the wrong password attempting to login + multiple times in short order might adversely impact the LDAP server. However, in + cases where the failure is, for example, due to a temporary network outage and + the `security-invalidation-interval` is relatively high then _not_ caching such + failures would be better. Users can enumerate any relevant exceptions which the + cache should ignore (e.g. `java.net.ConnectException`). The name of the exception + should be the **root cause** from the relevant stack-trace. Users can confirm + the configured exceptions are being skipped by enabling debug logging for + `org.apache.activemq.artemis.core.security.impl.SecurityStoreImpl`. - `debug` - boolean flag; if `true`, enable debugging; this is used only for testing or debugging; normally, it should be set to `false`, or omitted; diff --git a/examples/features/standard/security-manager/src/main/java/org/apache/activemq/artemis/jms/example/JAASSecurityManagerWrapper.java b/examples/features/standard/security-manager/src/main/java/org/apache/activemq/artemis/jms/example/JAASSecurityManagerWrapper.java index 132c55ff113..ef6e402ae2d 100644 --- a/examples/features/standard/security-manager/src/main/java/org/apache/activemq/artemis/jms/example/JAASSecurityManagerWrapper.java +++ b/examples/features/standard/security-manager/src/main/java/org/apache/activemq/artemis/jms/example/JAASSecurityManagerWrapper.java @@ -26,12 +26,13 @@ import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager; import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager5; +import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException; public class JAASSecurityManagerWrapper implements ActiveMQSecurityManager5 { ActiveMQJAASSecurityManager activeMQJAASSecurityManager; @Override - public Subject authenticate(String user, String password, RemotingConnection remotingConnection, String securityDomain) { + public Subject authenticate(String user, String password, RemotingConnection remotingConnection, String securityDomain) throws NoCacheLoginException { System.out.println("authenticate(" + user + ", " + password + ", " + remotingConnection.getRemoteAddress() + ", " + securityDomain + ")"); return activeMQJAASSecurityManager.authenticate(user, password, remotingConnection, securityDomain); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java index 2507971d7e2..1b34dd32812 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java @@ -142,7 +142,7 @@ public void testNoCacheException() throws Exception { public Subject authenticate(String user, String password, RemotingConnection remotingConnection, - String securityDomain) { + String securityDomain) throws NoCacheLoginException { flipper = !flipper; if (flipper) { return new Subject(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithClientIdValidationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithClientIdValidationTest.java index 070d87cecdc..261f475599a 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithClientIdValidationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompWithClientIdValidationTest.java @@ -29,6 +29,7 @@ import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager; import org.apache.activemq.artemis.spi.core.security.jaas.InVMLoginModule; +import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnection; import org.apache.activemq.artemis.tests.integration.stomp.util.StompClientConnectionFactory; import org.junit.Test; @@ -52,7 +53,7 @@ protected ActiveMQServer createServer() throws Exception { ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(InVMLoginModule.class.getName(), new SecurityConfiguration()) { @Override - public Subject authenticate(String user, String password, RemotingConnection remotingConnection, String securityDomain) { + public Subject authenticate(String user, String password, RemotingConnection remotingConnection, String securityDomain) throws NoCacheLoginException { Subject validatedUser = super.authenticate(user, password, remotingConnection, securityDomain); if (validatedUser == null) {