This is an automated email from the ASF dual-hosted git repository.

brusdev pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git


The following commit(s) were added to refs/heads/main by this push:
     new c2bada6a77 ARTEMIS-4267 original exception lost for 
NoCacheLoginException
c2bada6a77 is described below

commit c2bada6a770d9b0f6d19e5f3e137020c64954888
Author: Justin Bertram <[email protected]>
AuthorDate: Fri Apr 28 15:22:13 2023 -0500

    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 ----------
 .../core/remoting/impl/netty/NettyAcceptor.java    |  6 +-
 .../core/security/impl/SecurityStoreImpl.java      |  7 ++-
 .../core/security/ActiveMQJAASSecurityManager.java | 19 ++----
 .../core/security/ActiveMQSecurityManager5.java    |  3 +-
 .../spi/core/security/jaas/LDAPLoginModule.java    | 10 ++--
 .../core/security/jaas/NoCacheLoginException.java  |  8 ++-
 ...va => JAASSecurityManagerClassLoadingTest.java} | 28 ++++-----
 .../security/jaas/JAASSecurityManagerTest.java     | 70 ++++------------------
 .../core/security/jaas/NoCacheLoginModule.java     | 56 +++++++++++++++++
 artemis-server/src/test/resources/login.config     |  6 +-
 docs/user-manual/en/security.md                    | 15 +++--
 .../jms/example/JAASSecurityManagerWrapper.java    |  3 +-
 .../tests/integration/security/SecurityTest.java   |  2 +-
 .../stomp/StompWithClientIdValidationTest.java     |  3 +-
 15 files changed, 127 insertions(+), 142 deletions(-)

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 e9f5073a31..0000000000
--- 
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<Throwable> 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 a7ca627311..37cf6cfe9e 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.SSLContextConfig;
 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 class NettyAcceptor extends AbstractAcceptor {
                   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 class NettyAcceptor extends AbstractAcceptor {
       @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 d4301be5cf..7f770d9711 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 class SecurityStoreImpl implements SecurityStore, 
HierarchicalRepositoryC
                   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 @@ public class SecurityStoreImpl implements SecurityStore, 
HierarchicalRepositoryC
             
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 bcfcc923bc..e73ebaa6af 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 @@ package org.apache.activemq.artemis.spi.core.security;
 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.protocol.RemotingConnection;
 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 class ActiveMQJAASSecurityManager implements 
ActiveMQSecurityManager5 {
    }
 
    @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 @@ public class ActiveMQJAASSecurityManager implements 
ActiveMQSecurityManager5 {
          } 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 f3871d01af..2af3459e2d 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 java.util.Set;
 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 69d9b5a437..f8dc9692da 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.FailedLoginException;
 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.Queue;
 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 class LDAPLoginModule implements AuditLoginModule {
    }
 
    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 6db792f69a..3dea4c69fb 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/JAASSecurityManagerTest.java
 
b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java
similarity index 98%
copy from 
artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerTest.java
copy to 
artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java
index c8ded103c9..0482d1d37b 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/JAASSecurityManagerClassLoadingTest.java
@@ -17,36 +17,35 @@
 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.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.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 JAASSecurityManagerTest {
+public class JAASSecurityManagerClassLoadingTest {
    private static final Logger logger = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
    @Parameterized.Parameters(name = "newLoader=({0})")
@@ -102,5 +101,4 @@ public class JAASSecurityManagerTest {
          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 c8ded103c9..b37bdc1aa0 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<Object[]> data() {
-      return Arrays.asList(new Object[][] {{true}, {false}});
-   }
 
    static {
       String path = System.getProperty("java.security.auth.login.config");
@@ -69,38 +45,14 @@ public class JAASSecurityManagerTest {
       }
    }
 
-   @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<Role> 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 0000000000..536ee305bc
--- /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<String, ?> map, Map<String, ?> 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 106919bf41..ad1fa34cf2 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 004b6deafc..df66ea4a25 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 132c55ff11..ef6e402ae2 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.protocol.RemotingConnection;
 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 2507971d7e..1b34dd3281 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 class SecurityTest extends ActiveMQTestBase {
          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 070d87cecd..261f475599 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.core.server.ActiveMQServers;
 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 @@ public class StompWithClientIdValidationTest extends 
StompTestBase {
 
       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) {

Reply via email to