Skip to content

Commit

Permalink
ARTEMIS-4274 push masked credential support into core client
Browse files Browse the repository at this point in the history
  • Loading branch information
jbertram authored and clebertsuconic committed May 10, 2023
1 parent 5f47689 commit c9c819a
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -824,4 +824,8 @@ ClientSessionFactory createSessionFactory(TransportConfiguration transportConfig
ServerLocatorConfig getLocatorConfig();

void setLocatorConfig(ServerLocatorConfig serverLocatorConfig);

ServerLocator setPasswordCodec(String passwordCodec);

String getPasswordCodec();
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
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;
Expand Down Expand Up @@ -67,6 +68,7 @@
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;
Expand Down Expand Up @@ -738,16 +740,25 @@ private void failoverOrReconnect(final Object connectionID,
}
}

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.isBlockOnDurableSend(), serverLocator.isCacheLargeMessagesClient(), serverLocator.getMinLargeMessageSize(), serverLocator.isCompressLargeMessage(), serverLocator.getInitialMessagePacketSize(), serverLocator.getGroupID(), context, orderedExecutorFactory.getExecutor(), orderedExecutorFactory.getExecutor(), orderedExecutorFactory.getExecutor(), orderedExecutorFactory.getExecutor());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ public Set<ClientSessionFactoryInternal> getFactories() {

private ServerLocatorConfig config = new ServerLocatorConfig();

private String passwordCodec;

public static synchronized void clearThreadPools() {
ActiveMQClient.clearThreadPools();
}
Expand Down Expand Up @@ -301,6 +303,17 @@ public void setLocatorConfig(ServerLocatorConfig config) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
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;

Expand Down Expand Up @@ -830,12 +829,12 @@ public ActiveMQConnectionFactory setPassword(String password) {
}

public String getPasswordCodec() {
return passwordCodec;
return serverLocator.getPasswordCodec();
}

public ActiveMQConnectionFactory setPasswordCodec(String passwordCodec) {
checkWrite();
this.passwordCodec = passwordCodec;
serverLocator.setPasswordCodec(passwordCodec);
return this;
}

Expand Down Expand Up @@ -870,21 +869,8 @@ public int getFactoryType() {
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();
Expand All @@ -903,8 +889,6 @@ protected synchronized ActiveMQConnection createConnectionInternal(final String
}

ActiveMQConnection connection = null;
final String username = unmaskSensitiveString(rawUsername);
final String password = unmaskSensitiveString(rawPassword);

if (isXA) {
if (type == ActiveMQConnection.TYPE_GENERIC_CONNECTION) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
}

0 comments on commit c9c819a

Please sign in to comment.