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

clebertsuconic 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 c9c819aa88 ARTEMIS-4274 push masked credential support into core client
c9c819aa88 is described below

commit c9c819aa885bc8ababb8f956b59aefa90d727fb7
Author: Justin Bertram <[email protected]>
AuthorDate: Mon May 8 19:44:32 2023 -0500

    ARTEMIS-4274 push masked credential support into core client
---
 .../artemis/api/core/client/ServerLocator.java     |   4 +
 .../core/client/impl/ClientSessionFactoryImpl.java |  15 ++-
 .../core/client/impl/ServerLocatorImpl.java        |  13 ++
 .../jms/client/ActiveMQConnectionFactory.java      |  24 +---
 .../security/MaskedCredentialsTest.java            | 142 +++++++++++++++++++++
 5 files changed, 176 insertions(+), 22 deletions(-)

diff --git 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ServerLocator.java
 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ServerLocator.java
index 6120d60f6a..7cb94353ab 100644
--- 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ServerLocator.java
+++ 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/client/ServerLocator.java
@@ -824,4 +824,8 @@ public interface ServerLocator extends AutoCloseable {
    ServerLocatorConfig getLocatorConfig();
 
    void setLocatorConfig(ServerLocatorConfig serverLocatorConfig);
+
+   ServerLocator setPasswordCodec(String passwordCodec);
+
+   String getPasswordCodec();
 }
diff --git 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java
 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java
index 1f21d3351b..9b04a131d5 100644
--- 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java
+++ 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ClientSessionFactoryImpl.java
@@ -35,6 +35,7 @@ import java.util.concurrent.locks.ReentrantLock;
 import org.apache.activemq.artemis.api.config.ServerLocatorConfig;
 import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
 import org.apache.activemq.artemis.api.core.ActiveMQException;
+import org.apache.activemq.artemis.api.core.ActiveMQExceptionType;
 import org.apache.activemq.artemis.api.core.ActiveMQInterruptedException;
 import org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException;
 import org.apache.activemq.artemis.api.core.DisconnectReason;
@@ -67,6 +68,7 @@ import 
org.apache.activemq.artemis.spi.core.remoting.TopologyResponseHandler;
 import org.apache.activemq.artemis.utils.ClassloadingUtil;
 import org.apache.activemq.artemis.utils.ConfirmationWindowWarning;
 import org.apache.activemq.artemis.utils.ExecutorFactory;
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
 import org.apache.activemq.artemis.utils.UUIDGenerator;
 import org.apache.activemq.artemis.utils.actors.OrderedExecutorFactory;
 import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet;
@@ -738,16 +740,25 @@ public class ClientSessionFactoryImpl implements 
ClientSessionFactoryInternal, C
       }
    }
 
-   private ClientSession createSessionInternal(final String username,
-                                               final String password,
+   private ClientSession createSessionInternal(final String rawUsername,
+                                               final String rawPassword,
                                                final boolean xa,
                                                final boolean autoCommitSends,
                                                final boolean autoCommitAcks,
                                                final boolean preAcknowledge,
                                                final int ackBatchSize,
                                                final String clientID) throws 
ActiveMQException {
+      String username;
+      String password;
       String name = UUIDGenerator.getInstance().generateStringUUID();
 
+      try {
+         username = PasswordMaskingUtil.resolveMask(rawUsername, 
serverLocator.getPasswordCodec());
+         password = PasswordMaskingUtil.resolveMask(rawPassword, 
serverLocator.getPasswordCodec());
+      } catch (Exception e) {
+         throw new ActiveMQException(e.getMessage(), e, 
ActiveMQExceptionType.GENERIC_EXCEPTION);
+      }
+
       SessionContext context = createSessionChannel(name, username, password, 
xa, autoCommitSends, autoCommitAcks, preAcknowledge, clientID);
 
       ClientSessionInternal session = new ClientSessionImpl(this, name, 
username, password, xa, autoCommitSends, autoCommitAcks, preAcknowledge, 
serverLocator.isBlockOnAcknowledge(), serverLocator.isAutoGroup(), 
ackBatchSize, serverLocator.getConsumerWindowSize(), 
serverLocator.getConsumerMaxRate(), serverLocator.getConfirmationWindowSize(), 
serverLocator.getProducerWindowSize(), serverLocator.getProducerMaxRate(), 
serverLocator.isBlockOnNonDurableSend(), serverLocator.isBlockOnDurableSe [...]
diff --git 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java
 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java
index 346747f219..8084488270 100644
--- 
a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java
+++ 
b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/impl/ServerLocatorImpl.java
@@ -182,6 +182,8 @@ public final class ServerLocatorImpl implements 
ServerLocatorInternal, Discovery
 
    private ServerLocatorConfig config = new ServerLocatorConfig();
 
+   private String passwordCodec;
+
    public static synchronized void clearThreadPools() {
       ActiveMQClient.clearThreadPools();
    }
@@ -301,6 +303,17 @@ public final class ServerLocatorImpl implements 
ServerLocatorInternal, Discovery
       this.config = config;
    }
 
+   @Override
+   public ServerLocator setPasswordCodec(String passwordCodec) {
+      this.passwordCodec = passwordCodec;
+      return this;
+   }
+
+   @Override
+   public String getPasswordCodec() {
+      return this.passwordCodec;
+   }
+
    private static DiscoveryGroup createDiscoveryGroup(String nodeID,
                                                       
DiscoveryGroupConfiguration config) throws Exception {
       return new DiscoveryGroup(nodeID, config.getName(), 
config.getRefreshTimeout(), config.getBroadcastEndpointFactory(), null);
diff --git 
a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java
 
b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java
index 5506e84257..9c53e34311 100644
--- 
a/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java
+++ 
b/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQConnectionFactory.java
@@ -59,7 +59,6 @@ import 
org.apache.activemq.artemis.spi.core.remoting.ClientProtocolManagerFactor
 import org.apache.activemq.artemis.uri.ConnectionFactoryParser;
 import org.apache.activemq.artemis.uri.ServerLocatorParser;
 import org.apache.activemq.artemis.utils.ClassloadingUtil;
-import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
 import org.apache.activemq.artemis.utils.uri.BeanSupport;
 import org.apache.activemq.artemis.utils.uri.URISupport;
 
@@ -830,12 +829,12 @@ public class ActiveMQConnectionFactory extends 
JNDIStorable implements Connectio
    }
 
    public String getPasswordCodec() {
-      return passwordCodec;
+      return serverLocator.getPasswordCodec();
    }
 
    public ActiveMQConnectionFactory setPasswordCodec(String passwordCodec) {
       checkWrite();
-      this.passwordCodec = passwordCodec;
+      serverLocator.setPasswordCodec(passwordCodec);
       return this;
    }
 
@@ -870,21 +869,8 @@ public class ActiveMQConnectionFactory extends 
JNDIStorable implements Connectio
       return JMSFactoryType.CF.intValue();
    }
 
-   private String unmaskSensitiveString(String secret) throws JMSException {
-      try {
-         return PasswordMaskingUtil.resolveMask(secret, passwordCodec);
-      } catch (Exception e) {
-         JMSException jmse = new JMSException("Failed to resolve masked 
sensitive string");
-
-         jmse.initCause(e);
-         jmse.setLinkedException(e);
-
-         throw jmse;
-      }
-   }
-
-   protected synchronized ActiveMQConnection createConnectionInternal(final 
String rawUsername,
-                                                                      final 
String rawPassword,
+   protected synchronized ActiveMQConnection createConnectionInternal(final 
String username,
+                                                                      final 
String password,
                                                                       final 
boolean isXA,
                                                                       final 
int type) throws JMSException {
       makeReadOnly();
@@ -903,8 +889,6 @@ public class ActiveMQConnectionFactory extends JNDIStorable 
implements Connectio
       }
 
       ActiveMQConnection connection = null;
-      final String username = unmaskSensitiveString(rawUsername);
-      final String password = unmaskSensitiveString(rawPassword);
 
       if (isXA) {
          if (type == ActiveMQConnection.TYPE_GENERIC_CONNECTION) {
diff --git 
a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/MaskedCredentialsTest.java
 
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/MaskedCredentialsTest.java
new file mode 100644
index 0000000000..f9a67fbaa4
--- /dev/null
+++ 
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/MaskedCredentialsTest.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.activemq.artemis.tests.integration.security;
+
+import java.lang.management.ManagementFactory;
+import java.net.URL;
+
+import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
+import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
+import org.apache.activemq.artemis.api.core.client.ServerLocator;
+import org.apache.activemq.artemis.core.server.ActiveMQServer;
+import org.apache.activemq.artemis.core.server.ActiveMQServers;
+import 
org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
+import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
+import org.apache.activemq.artemis.utils.SensitiveDataCodec;
+import org.junit.Before;
+import org.junit.Test;
+
+public class MaskedCredentialsTest extends ActiveMQTestBase {
+
+   static {
+      String path = System.getProperty("java.security.auth.login.config");
+      if (path == null) {
+         URL resource = 
MaskedCredentialsTest.class.getClassLoader().getResource("login.config");
+         if (resource != null) {
+            path = resource.getFile();
+            System.setProperty("java.security.auth.login.config", path);
+         }
+      }
+   }
+
+   private ServerLocator locator;
+
+   ClientSessionFactory cf;
+
+   @Override
+   @Before
+   public void setUp() throws Exception {
+      super.setUp();
+
+      ActiveMQJAASSecurityManager securityManager = new 
ActiveMQJAASSecurityManager("PropertiesLogin");
+      ActiveMQServer server = 
addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true),
 ManagementFactory.getPlatformMBeanServer(), securityManager, false));
+      server.start();
+
+      locator = createInVMNonHALocator();
+      cf = createSessionFactory(locator);
+   }
+
+   @Test
+   public void testMaskedCredentials() throws Exception {
+      addClientSession(cf.createSession(getMaskedCredential("first"), 
getMaskedCredential("secret"), false, true, true, false, 0));
+   }
+
+   @Test
+   public void testMaskedCredentialsWithCustomCodec() throws Exception {
+      testMaskedCredentialsWithCustomCodec("secret");
+   }
+
+   @Test
+   public void testMaskedCredentialsWithCustomCodecNegative() throws Exception 
{
+      try {
+         testMaskedCredentialsWithCustomCodec("xxx");
+         fail();
+      } catch (Exception e) {
+         // expected
+      }
+   }
+
+   private void testMaskedCredentialsWithCustomCodec(String password) throws 
Exception {
+      ServerLocator locator = createInVMNonHALocator();
+      locator.setPasswordCodec(DummyCodec.class.getName());
+      testMaskedCredentialsWithCustomCodec(locator, password);
+   }
+
+   @Test
+   public void testMaskedCredentialsWithCustomCodecURL() throws Exception {
+      testMaskedCredentialsWithCustomCodecURL("secret");
+   }
+
+   @Test
+   public void testMaskedCredentialsWithCustomCodecNegativeURL() throws 
Exception {
+      try {
+         testMaskedCredentialsWithCustomCodecURL("xxx");
+         fail();
+      } catch (Exception e) {
+         // expected
+      }
+   }
+
+   private void testMaskedCredentialsWithCustomCodecURL(String password) 
throws Exception {
+      ServerLocator locator = 
ActiveMQClient.createServerLocator("vm://0?passwordCodec=org.apache.activemq.artemis.tests.integration.security.MaskedCredentialsTest.DummyCodec");
+      locator.setPasswordCodec(DummyCodec.class.getName());
+      testMaskedCredentialsWithCustomCodec(locator, password);
+   }
+
+   private void testMaskedCredentialsWithCustomCodec(ServerLocator locator, 
String password) throws Exception {
+      cf = createSessionFactory(locator);
+      String maskedPassword = 
PasswordMaskingUtil.wrap(PasswordMaskingUtil.resolveMask(password, 
DummyCodec.class.getName()));
+      addClientSession(cf.createSession("first", maskedPassword, false, true, 
true, false, 0));
+   }
+
+   private String getMaskedCredential(String credential) throws Exception {
+      return 
PasswordMaskingUtil.wrap(PasswordMaskingUtil.getDefaultCodec().encode(credential));
+   }
+
+   public static class DummyCodec implements SensitiveDataCodec<String> {
+
+      private static final String MASK = "===";
+      private static final String CLEARTEXT = "secret";
+
+      @Override
+      public String decode(Object mask) throws Exception {
+         if (!MASK.equals(mask)) {
+            return mask.toString();
+         }
+         return CLEARTEXT;
+      }
+
+      @Override
+      public String encode(Object secret) throws Exception {
+         if (!CLEARTEXT.equals(secret)) {
+            return secret.toString();
+         }
+         return MASK;
+      }
+   }
+}

Reply via email to