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;
+ }
+ }
+}