ARTEMIS-1600 Support masked passwords in bootstrap.xm and login.config We provide a feature to mask passwords in the configuration files. However, passwords in the bootstrap.xml (when the console is secured with HTTPS) cannot be masked. This enhancement has been opened to allow passwords in the bootstrap.xml to be masked using the built-in masking feature provided by the broker.
Also the LDAPLoginModule configuration (in login.config) has a connection password attribute that also needs this mask support. In addition the ENC() syntax is supported for password masking to replace the old 'mask-password' flag. Project: http://git-wip-us.apache.org/repos/asf/activemq-artemis/repo Commit: http://git-wip-us.apache.org/repos/asf/activemq-artemis/commit/bb84f679 Tree: http://git-wip-us.apache.org/repos/asf/activemq-artemis/tree/bb84f679 Diff: http://git-wip-us.apache.org/repos/asf/activemq-artemis/diff/bb84f679 Branch: refs/heads/master Commit: bb84f679363f62e8b2663f63bf23f04133de481d Parents: 1d31227 Author: Howard Gao <howard....@gmail.com> Authored: Thu Jan 18 14:31:32 2018 +0800 Committer: Justin Bertram <jbert...@apache.org> Committed: Thu Jan 18 08:59:00 2018 -0600 ---------------------------------------------------------------------- .../apache/activemq/cli/test/HashUtilTest.java | 47 ++++++ .../artemis/utils/PasswordMaskingUtil.java | 48 +++++- .../artemis/utils/SecureHashProcessor.java | 7 +- .../artemis/utils/SensitiveDataCodec.java | 3 +- .../utils/MaskPasswordResolvingTest.java | 116 ++++++++++++++ .../config/ActiveMQDefaultConfiguration.java | 4 +- .../client/ActiveMQClientMessageBundle.java | 6 - .../artemis/utils/ConfigurationHelper.java | 19 +-- artemis-dto/pom.xml | 8 + .../activemq/artemis/dto/WebServerDTO.java | 35 ++++- .../artemis/jms/bridge/impl/JMSBridgeImpl.java | 27 +--- .../artemis/ra/ActiveMQRAProperties.java | 28 +--- .../artemis/ra/ActiveMQResourceAdapter.java | 5 +- .../artemis/ra/inflow/ActiveMQActivation.java | 18 +-- .../artemis/core/config/Configuration.java | 4 +- .../core/config/impl/ConfigurationImpl.java | 23 ++- .../config/impl/FileSecurityConfiguration.java | 16 +- .../deployers/impl/FileConfigurationParser.java | 37 ++--- .../spi/core/security/jaas/LDAPLoginModule.java | 37 ++++- .../artemis/utils/XMLConfigurationUtil.java | 2 +- .../impl/FileConfigurationParserTest.java | 82 ++++++++++ .../jaas/LDAPLoginModuleMaskPasswordTest.java | 156 +++++++++++++++++++ .../jaas/PropertiesLoginModuleTest.java | 7 + artemis-server/src/test/resources/login.config | 59 +++++++ .../src/test/resources/users.properties | 1 + artemis-web/pom.xml | 7 + .../artemis/component/WebServerComponent.java | 4 +- .../cli/test/WebServerComponentTest.java | 51 +++++- .../src/test/resources/bootstrap_secure_web.xml | 34 ++++ .../src/test/resources/bootstrap_web.xml | 34 ++++ .../src/test/resources/bootstrap_web_codec.xml | 34 ++++ docs/user-manual/en/masking-passwords.md | 141 +++++++++++++++-- .../integration/ra/ResourceAdapterTest.java | 84 +++++++++- .../jms/bridge/impl/JMSBridgeImplTest.java | 39 +++++ 34 files changed, 1057 insertions(+), 166 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-cli/src/test/java/org/apache/activemq/cli/test/HashUtilTest.java ---------------------------------------------------------------------- diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/HashUtilTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/HashUtilTest.java new file mode 100644 index 0000000..09a5208 --- /dev/null +++ b/artemis-cli/src/test/java/org/apache/activemq/cli/test/HashUtilTest.java @@ -0,0 +1,47 @@ +/* + * 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.cli.test; + +import org.apache.activemq.artemis.cli.commands.util.HashUtil; +import org.apache.activemq.artemis.utils.PasswordMaskingUtil; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class HashUtilTest { + + @Test + public void testDefaultHashFormat() throws Exception { + final String password = "helloworld"; + String hash = HashUtil.tryHash(new TestActionContext(), password); + String hashStr = PasswordMaskingUtil.unwrap(hash); + System.out.println("hashString: " + hashStr); + String[] parts = hashStr.split(":"); + assertEquals(3, parts.length); + //first part should be able to convert to an int + Integer.parseInt(parts[0]); + //second and third parts are all hex values + checkHexBytes(parts[1], parts[2]); + } + + private void checkHexBytes(String... parts) throws Exception { + for (String p : parts) { + assertTrue(p.matches("^[0-9A-F]+$")); + } + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java ---------------------------------------------------------------------- diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java index dbc20c5..be56046 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java @@ -28,14 +28,54 @@ import org.apache.activemq.artemis.logs.ActiveMQUtilBundle; public final class PasswordMaskingUtil { + public static final String BEGIN_ENC = "ENC("; + public static final String END_ENC = ")"; + private PasswordMaskingUtil() { } + /** + * This method deals with password masking and returns the password in its plain text form. + * @param maskPassword : explicit mask flag. If it's true, the password is interpreted as + * masked. If it is false, the password is interpreted as plain text. + * if it is null, the password will be interpreted as masked if the + * password is wrapped in ENC(), or as plain text otherwise. + * @param password : the original value of password string + * @param codecClass : the codec used to decode the password. Only when the password is interpreted + * as masked will this codec be used. Ignored otherwise. + * @return + */ + public static String resolveMask(Boolean maskPassword, String password, String codecClass) throws Exception { + String plainText = password; + if (maskPassword == null) { + if (isEncMasked(password)) { + //masked + String bareMaskedPassword = unwrap(password); + plainText = getCodec(codecClass).decode(bareMaskedPassword); + } + } else if (maskPassword) { + plainText = getCodec(codecClass).decode(password); + } + return plainText; + } + + public static boolean isEncMasked(String password) { + return (password.startsWith(BEGIN_ENC) && password.endsWith(END_ENC)); + } + + //remove ENC() from the password body + public static String unwrap(String password) { + return password.substring(4, password.length() - 1); + } + + public static String wrap(String password) { + return BEGIN_ENC + password + END_ENC; + } + private static final class LazyPlainTextProcessorHolder { private LazyPlainTextProcessorHolder() { - } private static final HashProcessor INSTANCE = new NoHashProcessor(); @@ -83,7 +123,7 @@ public final class PasswordMaskingUtil { } private static boolean isEncoded(String storedPassword) { - return storedPassword == null || (storedPassword.startsWith(SecureHashProcessor.BEGIN_HASH) && storedPassword.endsWith(SecureHashProcessor.END_HASH)); + return storedPassword == null || (isEncMasked(storedPassword)); } public static HashProcessor getHashProcessor() { @@ -107,6 +147,10 @@ public final class PasswordMaskingUtil { public static SensitiveDataCodec<String> getCodec(String codecDesc) throws ActiveMQException { SensitiveDataCodec<String> codecInstance; + if (codecDesc == null) { + return getDefaultCodec(); + } + // semi colons String[] parts = codecDesc.split(";"); if (parts.length < 1) http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java ---------------------------------------------------------------------- diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java index 8e9cd5c..a17cfd4 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java @@ -18,9 +18,6 @@ package org.apache.activemq.artemis.utils; public class SecureHashProcessor implements HashProcessor { - public static final String BEGIN_HASH = "ENC("; - public static final String END_HASH = ")"; - private DefaultSensitiveStringCodec codec; public SecureHashProcessor(DefaultSensitiveStringCodec codec) { @@ -29,13 +26,13 @@ public class SecureHashProcessor implements HashProcessor { @Override public String hash(String plainText) throws Exception { - return BEGIN_HASH + codec.encode(plainText) + END_HASH; + return PasswordMaskingUtil.wrap(codec.encode(plainText)); } @Override //storedValue must take form of ENC(...) public boolean compare(char[] inputValue, String storedValue) { - String storedHash = storedValue.substring(4, storedValue.length() - 2); + String storedHash = storedValue.substring(4, storedValue.length() - 1); return codec.verify(inputValue, storedHash); } } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java ---------------------------------------------------------------------- diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java index d585976..cbd17e5 100644 --- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java +++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SensitiveDataCodec.java @@ -31,5 +31,6 @@ public interface SensitiveDataCodec<T> { T encode(Object secret) throws Exception; - void init(Map<String, String> params) throws Exception; + default void init(Map<String, String> params) throws Exception { + }; } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/MaskPasswordResolvingTest.java ---------------------------------------------------------------------- diff --git a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/MaskPasswordResolvingTest.java b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/MaskPasswordResolvingTest.java new file mode 100644 index 0000000..5a99ccc --- /dev/null +++ b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/MaskPasswordResolvingTest.java @@ -0,0 +1,116 @@ +/* + * 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 org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +@RunWith(Parameterized.class) +public class MaskPasswordResolvingTest { + + private static final String plainPassword = "password"; + private static final String defaultMaskPassword = "defaultmasked"; + private static final String customizedCodecPassword = "secret"; + private static final String oldDefaultMaskedPassword = "oldmasked"; + private static final String oldCustomizedCodecPassword = "secret"; + private static final String oldExplicitPlainPassword = "PASSWORD"; + + @Parameterized.Parameters(name = "mask({0})password({1})codec({2})") + public static Collection<Object[]> params() { + return Arrays.asList(new Object[][]{{null, plainPassword, null}, + {null, "ENC(3bdfd94fe8cdf710e7fefa72f809ea90)", null}, + {null, "ENC(momsword)", "org.apache.activemq.artemis.utils.MaskPasswordResolvingTest$SimplePasswordCodec"}, + {true, "662d05f5a83f9e073af6b8dc081d34aa", null}, + {true, "momsword", "org.apache.activemq.artemis.utils.MaskPasswordResolvingTest$SimplePasswordCodec"}, + {false, oldExplicitPlainPassword, null}, + {false, oldExplicitPlainPassword, "org.apache.activemq.artemis.utils.MaskPasswordResolvingTest$SimplePasswordCodec"}}); + } + + private Boolean maskPassword; + private String password; + private String codec; + + public MaskPasswordResolvingTest(Boolean maskPassword, String password, String codec) { + this.maskPassword = maskPassword; + this.password = password; + this.codec = codec; + } + + @Test + public void testPasswordResolving() throws Exception { + String resolved = PasswordMaskingUtil.resolveMask(maskPassword, password, codec); + System.out.println("resolved: " + resolved); + checkResult(resolved); + } + + private void checkResult(String resolved) throws Exception { + if (this.maskPassword == null) { + if (PasswordMaskingUtil.isEncMasked(this.password)) { + if (this.codec != null) { + assertEquals(customizedCodecPassword, resolved); + } else { + assertEquals(defaultMaskPassword, resolved); + } + } else { + assertEquals(plainPassword, resolved); + } + } else { + if (this.maskPassword) { + if (this.codec != null) { + assertEquals(oldCustomizedCodecPassword, resolved); + } else { + assertEquals(oldDefaultMaskedPassword, resolved); + } + } else { + assertEquals(oldExplicitPlainPassword, resolved); + } + } + } + + public static class SimplePasswordCodec implements SensitiveDataCodec<String> { + + private Map<String, String> passwordBook = new HashMap<>(); + + public SimplePasswordCodec() { + passwordBook.put("momsword", "secret"); + passwordBook.put("youneverknow", "keypass"); + passwordBook.put("youcanguess", "trustpass"); + } + + @Override + public String decode(Object mask) throws Exception { + String password = passwordBook.get(mask); + if (password == null) { + throw new IllegalArgumentException("I don't know the password " + mask); + } + return password; + } + + @Override + public String encode(Object secret) throws Exception { + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java ---------------------------------------------------------------------- diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java index 9b5d75d..fecf71c 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java @@ -179,7 +179,7 @@ public final class ActiveMQDefaultConfiguration { private static String DEFAULT_CLUSTER_PASSWORD = "CHANGE ME!!"; // This option controls whether passwords in server configuration need be masked. If set to "true" the passwords are masked. - private static boolean DEFAULT_MASK_PASSWORD = false; + private static Boolean DEFAULT_MASK_PASSWORD = null; // true means that the management API is available via JMX private static boolean DEFAULT_JMX_MANAGEMENT_ENABLED = true; @@ -622,7 +622,7 @@ public final class ActiveMQDefaultConfiguration { /** * This option controls whether passwords in server configuration need be masked. If set to "true" the passwords are masked. */ - public static boolean isDefaultMaskPassword() { + public static Boolean isDefaultMaskPassword() { return DEFAULT_MASK_PASSWORD; } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientMessageBundle.java ---------------------------------------------------------------------- diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientMessageBundle.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientMessageBundle.java index 64107b9..bb88e6d 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientMessageBundle.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/client/ActiveMQClientMessageBundle.java @@ -178,9 +178,6 @@ public interface ActiveMQClientMessageBundle { @Message(id = 119043, value = "Invalid argument null handler") IllegalArgumentException nullHandler(); - @Message(id = 119044, value = "No available codec to decode password!") - IllegalArgumentException noCodec(); - @Message(id = 119045, value = "the first node to be compared is null") IllegalArgumentException firstNodeNull(); @@ -214,9 +211,6 @@ public interface ActiveMQClientMessageBundle { @Message(id = 119055, value = "Element {0} requires a valid Long value, but ''{1}'' cannot be parsed as a Long", format = Message.Format.MESSAGE_FORMAT) IllegalArgumentException mustBeLong(Node element, String value); - @Message(id = 119056, value = "Failed to get decoder") - IllegalArgumentException failedToGetDecoder(@Cause Exception e); - @Message(id = 119057, value = "Error decoding password") IllegalArgumentException errordecodingPassword(@Cause Exception e); http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/ConfigurationHelper.java ---------------------------------------------------------------------- diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/ConfigurationHelper.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/ConfigurationHelper.java index a205c19..4367318 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/ConfigurationHelper.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/ConfigurationHelper.java @@ -20,7 +20,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import org.apache.activemq.artemis.api.core.ActiveMQException; import org.apache.activemq.artemis.core.client.ActiveMQClientLogger; import org.apache.activemq.artemis.core.client.ActiveMQClientMessageBundle; @@ -164,25 +163,9 @@ public class ConfigurationHelper { String value = prop.toString(); Boolean useMask = (Boolean) props.get(defaultMaskPassword); - if (useMask == null || (!useMask)) { - return value; - } - final String classImpl = (String) props.get(defaultPasswordCodec); - - if (classImpl == null) { - throw ActiveMQClientMessageBundle.BUNDLE.noCodec(); - } - - SensitiveDataCodec<String> codec = null; - try { - codec = PasswordMaskingUtil.getCodec(classImpl); - } catch (ActiveMQException e1) { - throw ActiveMQClientMessageBundle.BUNDLE.failedToGetDecoder(e1); - } - try { - return codec.decode(value); + return PasswordMaskingUtil.resolveMask(useMask, value, classImpl); } catch (Exception e) { throw ActiveMQClientMessageBundle.BUNDLE.errordecodingPassword(e); } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-dto/pom.xml ---------------------------------------------------------------------- diff --git a/artemis-dto/pom.xml b/artemis-dto/pom.xml index 05de601..f875957 100644 --- a/artemis-dto/pom.xml +++ b/artemis-dto/pom.xml @@ -31,6 +31,14 @@ <activemq.basedir>${project.basedir}/..</activemq.basedir> </properties> + <dependencies> + <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-commons</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + <build> <resources> <resource> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java ---------------------------------------------------------------------- diff --git a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java index 4553e0a..3e15261 100644 --- a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java +++ b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java @@ -16,6 +16,8 @@ */ package org.apache.activemq.artemis.dto; +import org.apache.activemq.artemis.utils.PasswordMaskingUtil; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; @@ -37,21 +39,44 @@ public class WebServerDTO extends ComponentDTO { public Boolean clientAuth; @XmlAttribute - public String keyStorePath; + public String passwordCodec; @XmlAttribute - public String keyStorePassword; + public String keyStorePath; @XmlAttribute public String trustStorePath; - @XmlAttribute - public String trustStorePassword; - @XmlElementRef public List<AppDTO> apps; + @XmlAttribute + private String keyStorePassword; + + @XmlAttribute + private String trustStorePassword; + public WebServerDTO() { componentClassName = "org.apache.activemq.artemis.component.WebServerComponent"; } + + public String getKeyStorePassword() throws Exception { + return getPassword(this.keyStorePassword); + } + + private String getPassword(String password) throws Exception { + return PasswordMaskingUtil.resolveMask(null, password, this.passwordCodec); + } + + public void setKeyStorePassword(String keyStorePassword) { + this.keyStorePassword = keyStorePassword; + } + + public String getTrustStorePassword() throws Exception { + return getPassword(this.trustStorePassword); + } + + public void setTrustStorePassword(String trustStorePassword) { + this.trustStorePassword = trustStorePassword; + } } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java ---------------------------------------------------------------------- diff --git a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java index b67b0b3..4d9b04e 100644 --- a/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java +++ b/artemis-jms-server/src/main/java/org/apache/activemq/artemis/jms/bridge/impl/JMSBridgeImpl.java @@ -69,9 +69,7 @@ import org.apache.activemq.artemis.jms.server.ActiveMQJMSServerBundle; import org.apache.activemq.artemis.service.extensions.ServiceUtils; import org.apache.activemq.artemis.service.extensions.xa.recovery.ActiveMQRegistry; import org.apache.activemq.artemis.service.extensions.xa.recovery.XARecoveryConfig; -import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec; import org.apache.activemq.artemis.utils.PasswordMaskingUtil; -import org.apache.activemq.artemis.utils.SensitiveDataCodec; public final class JMSBridgeImpl implements JMSBridge { @@ -168,7 +166,7 @@ public final class JMSBridgeImpl implements JMSBridge { private ObjectName objectName; - private boolean useMaskedPassword = false; + private Boolean useMaskedPassword; private String passwordCodec; @@ -440,25 +438,16 @@ public final class JMSBridgeImpl implements JMSBridge { } private void initPasswords() throws ActiveMQException { - if (useMaskedPassword) { - SensitiveDataCodec<String> codecInstance = new DefaultSensitiveStringCodec(); - - if (passwordCodec != null) { - codecInstance = PasswordMaskingUtil.getCodec(passwordCodec); + try { + if (this.sourcePassword != null) { + sourcePassword = PasswordMaskingUtil.resolveMask(useMaskedPassword, sourcePassword, passwordCodec); } - try { - if (this.sourcePassword != null) { - sourcePassword = codecInstance.decode(sourcePassword); - } - - if (this.targetPassword != null) { - targetPassword = codecInstance.decode(targetPassword); - } - } catch (Exception e) { - throw ActiveMQJMSServerBundle.BUNDLE.errorDecodingPassword(e); + if (this.targetPassword != null) { + targetPassword = PasswordMaskingUtil.resolveMask(useMaskedPassword, targetPassword, passwordCodec); } - + } catch (Exception e) { + throw ActiveMQJMSServerBundle.BUNDLE.errorDecodingPassword(e); } } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java ---------------------------------------------------------------------- diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java index dffd5b3..e56e80d 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQRAProperties.java @@ -20,9 +20,7 @@ import java.io.Serializable; import java.util.Hashtable; import org.apache.activemq.artemis.api.core.ActiveMQException; -import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec; import org.apache.activemq.artemis.utils.PasswordMaskingUtil; -import org.apache.activemq.artemis.utils.SensitiveDataCodec; /** * The RA default properties - these are set in the ra.xml file @@ -66,14 +64,12 @@ public class ActiveMQRAProperties extends ConnectionFactoryProperties implements private boolean useJNDI; - private boolean useMaskedPassword = false; + private Boolean useMaskedPassword = null; private String passwordCodec; private boolean initialized = false; - private transient SensitiveDataCodec<String> codecInstance; - /** * Class used to get a JChannel */ @@ -217,11 +213,11 @@ public class ActiveMQRAProperties extends ConnectionFactoryProperties implements this.setupInterval = setupInterval; } - public boolean isUseMaskedPassword() { + public Boolean isUseMaskedPassword() { return useMaskedPassword; } - public void setUseMaskedPassword(boolean useMaskedPassword) { + public void setUseMaskedPassword(Boolean useMaskedPassword) { this.useMaskedPassword = useMaskedPassword; } @@ -243,27 +239,19 @@ public class ActiveMQRAProperties extends ConnectionFactoryProperties implements if (initialized) return; - if (useMaskedPassword) { - codecInstance = new DefaultSensitiveStringCodec(); - - if (passwordCodec != null) { - codecInstance = PasswordMaskingUtil.getCodec(passwordCodec); - } - + if (password != null) { try { - if (password != null) { - password = codecInstance.decode(password); - } + password = PasswordMaskingUtil.resolveMask(useMaskedPassword, password, passwordCodec); } catch (Exception e) { throw ActiveMQRABundle.BUNDLE.errorDecodingPassword(e); } - } + initialized = true; } - public SensitiveDataCodec<String> getCodecInstance() { - return codecInstance; + public String getCodec() { + return passwordCodec; } public String getJgroupsChannelLocatorClass() { http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java ---------------------------------------------------------------------- diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java index a43d37b..684792e 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/ActiveMQResourceAdapter.java @@ -59,7 +59,6 @@ import org.apache.activemq.artemis.ra.inflow.ActiveMQActivationSpec; import org.apache.activemq.artemis.ra.recovery.RecoveryManager; import org.apache.activemq.artemis.service.extensions.ServiceUtils; import org.apache.activemq.artemis.service.extensions.xa.recovery.XARecoveryConfig; -import org.apache.activemq.artemis.utils.SensitiveDataCodec; import org.jboss.logging.Logger; import org.jgroups.JChannel; @@ -2037,8 +2036,8 @@ public class ActiveMQResourceAdapter implements ResourceAdapter, Serializable { managedConnectionFactories.add(activeMQRAManagedConnectionFactory); } - public SensitiveDataCodec<String> getCodecInstance() { - return raProperties.getCodecInstance(); + public String getCodec() { + return raProperties.getCodec(); } public synchronized void closeConnectionFactory(ConnectionFactoryProperties properties) { http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java ---------------------------------------------------------------------- diff --git a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java index dc4d648..97498a2 100644 --- a/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java +++ b/artemis-ra/src/main/java/org/apache/activemq/artemis/ra/inflow/ActiveMQActivation.java @@ -57,7 +57,7 @@ import org.apache.activemq.artemis.ra.ActiveMQRaUtils; import org.apache.activemq.artemis.ra.ActiveMQResourceAdapter; import org.apache.activemq.artemis.service.extensions.xa.recovery.XARecoveryConfig; import org.apache.activemq.artemis.utils.FutureLatch; -import org.apache.activemq.artemis.utils.SensitiveDataCodec; +import org.apache.activemq.artemis.utils.PasswordMaskingUtil; import org.jboss.logging.Logger; /** @@ -148,16 +148,12 @@ public class ActiveMQActivation { logger.trace("constructor(" + ra + ", " + endpointFactory + ", " + spec + ")"); } - if (ra.isUseMaskedPassword()) { - String pass = spec.getOwnPassword(); - if (pass != null) { - SensitiveDataCodec<String> codec = ra.getCodecInstance(); - - try { - spec.setPassword(codec.decode(pass)); - } catch (Exception e) { - throw new ResourceException(e); - } + String pass = spec.getOwnPassword(); + if (pass != null) { + try { + spec.setPassword(PasswordMaskingUtil.resolveMask(ra.isUseMaskedPassword(), pass, ra.getCodec())); + } catch (Exception e) { + throw new ResourceException(e); } } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java index 6f44bbb..f052248 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java @@ -1000,12 +1000,12 @@ public interface Configuration { /** * Sets if passwords should be masked or not. True means the passwords should be masked. */ - Configuration setMaskPassword(boolean maskPassword); + Configuration setMaskPassword(Boolean maskPassword); /** * If passwords are masked. True means the passwords are masked. */ - boolean isMaskPassword(); + Boolean isMaskPassword(); /* * Whether or not that ActiveMQ Artemis should use all protocols available on the classpath. If false only the core protocol will http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java index ff68f15..93085f9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java @@ -249,7 +249,7 @@ public class ConfigurationImpl implements Configuration, Serializable { protected List<ConnectorServiceConfiguration> connectorServiceConfigurations = new ArrayList<>(); - private boolean maskPassword = ActiveMQDefaultConfiguration.isDefaultMaskPassword(); + private Boolean maskPassword = ActiveMQDefaultConfiguration.isDefaultMaskPassword(); private transient String passwordCodec; @@ -1461,12 +1461,12 @@ public class ConfigurationImpl implements Configuration, Serializable { } @Override - public boolean isMaskPassword() { + public Boolean isMaskPassword() { return maskPassword; } @Override - public ConfigurationImpl setMaskPassword(boolean maskPassword) { + public ConfigurationImpl setMaskPassword(Boolean maskPassword) { this.maskPassword = maskPassword; return this; } @@ -1673,7 +1673,6 @@ public class ConfigurationImpl implements Configuration, Serializable { return false; if (asyncConnectionExecutionEnabled != other.asyncConnectionExecutionEnabled) return false; - if (bindingsDirectory == null) { if (other.bindingsDirectory != null) return false; @@ -1689,11 +1688,13 @@ public class ConfigurationImpl implements Configuration, Serializable { return false; } else if (!broadcastGroupConfigurations.equals(other.broadcastGroupConfigurations)) return false; + if (clusterConfigurations == null) { if (other.clusterConfigurations != null) return false; } else if (!clusterConfigurations.equals(other.clusterConfigurations)) return false; + if (clusterPassword == null) { if (other.clusterPassword != null) return false; @@ -1720,6 +1721,7 @@ public class ConfigurationImpl implements Configuration, Serializable { return false; if (createJournalDir != other.createJournalDir) return false; + if (discoveryGroupConfigurations == null) { if (other.discoveryGroupConfigurations != null) return false; @@ -1801,8 +1803,15 @@ public class ConfigurationImpl implements Configuration, Serializable { return false; } else if (!managementNotificationAddress.equals(other.managementNotificationAddress)) return false; - if (maskPassword != other.maskPassword) - return false; + + if (this.maskPassword == null) { + if (other.maskPassword != null) + return false; + } else { + if (!this.maskPassword.equals(other.maskPassword)) + return false; + } + if (maxConcurrentPageIO != other.maxConcurrentPageIO) return false; if (memoryMeasureInterval != other.memoryMeasureInterval) @@ -1824,7 +1833,6 @@ public class ConfigurationImpl implements Configuration, Serializable { return false; } else if (!name.equals(other.name)) return false; - if (outgoingInterceptorClassNames == null) { if (other.outgoingInterceptorClassNames != null) return false; @@ -1881,6 +1889,7 @@ public class ConfigurationImpl implements Configuration, Serializable { if (journalDatasync != other.journalDatasync) { return false; } + if (globalMaxSize != null && !globalMaxSize.equals(other.globalMaxSize)) { return false; } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/FileSecurityConfiguration.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/FileSecurityConfiguration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/FileSecurityConfiguration.java index d778aa5..b33a419 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/FileSecurityConfiguration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/FileSecurityConfiguration.java @@ -22,7 +22,6 @@ import java.util.Set; import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; import org.apache.activemq.artemis.utils.PasswordMaskingUtil; -import org.apache.activemq.artemis.utils.SensitiveDataCodec; @Deprecated public class FileSecurityConfiguration extends SecurityConfiguration { @@ -31,7 +30,7 @@ public class FileSecurityConfiguration extends SecurityConfiguration { private final String rolesUrl; - private boolean maskPassword; + private Boolean maskPassword; private String passwordCodec; @@ -65,14 +64,7 @@ public class FileSecurityConfiguration extends SecurityConfiguration { if (started) { return; } - SensitiveDataCodec<String> codec = null; - if (maskPassword) { - if (passwordCodec != null) { - codec = PasswordMaskingUtil.getDefaultCodec(); - } else { - codec = PasswordMaskingUtil.getCodec(passwordCodec); - } - } + URL theUsersUrl = getClass().getClassLoader().getResource(usersUrl); if (theUsersUrl == null) { @@ -94,9 +86,7 @@ public class FileSecurityConfiguration extends SecurityConfiguration { for (String username : keys) { String password = userProps.getProperty(username); - if (codec != null) { - password = codec.decode(password); - } + password = PasswordMaskingUtil.resolveMask(this.maskPassword, password, passwordCodec); addUser(username, password); } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index 9b9050b..14b0025 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -80,7 +80,6 @@ import org.apache.activemq.artemis.utils.ByteUtil; import org.apache.activemq.artemis.utils.ClassloadingUtil; import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec; import org.apache.activemq.artemis.utils.PasswordMaskingUtil; -import org.apache.activemq.artemis.utils.SensitiveDataCodec; import org.apache.activemq.artemis.utils.XMLConfigurationUtil; import org.apache.activemq.artemis.utils.XMLUtil; import org.apache.activemq.artemis.utils.critical.CriticalAnalyzerPolicy; @@ -339,7 +338,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { config.setManagementNotificationAddress(new SimpleString(getString(e, "management-notification-address", config.getManagementNotificationAddress().toString(), Validators.NOT_NULL_OR_EMPTY))); - config.setMaskPassword(getBoolean(e, "mask-password", false)); + config.setMaskPassword(getBoolean(e, "mask-password", null)); config.setPasswordCodec(getString(e, "password-codec", DefaultSensitiveStringCodec.class.getName(), Validators.NOT_NULL_OR_EMPTY)); @@ -369,15 +368,11 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { // parsing cluster password String passwordText = getString(e, "cluster-password", null, Validators.NO_CHECK); - final boolean maskText = config.isMaskPassword(); + final Boolean maskText = config.isMaskPassword(); if (passwordText != null) { - if (maskText) { - SensitiveDataCodec<String> codec = PasswordMaskingUtil.getCodec(config.getPasswordCodec()); - config.setClusterPassword(codec.decode(passwordText)); - } else { - config.setClusterPassword(passwordText); - } + String resolvedPassword = PasswordMaskingUtil.resolveMask(maskText, passwordText, config.getPasswordCodec()); + config.setClusterPassword(resolvedPassword); } config.setClusterUser(getString(e, "cluster-user", config.getClusterUser(), Validators.NO_CHECK)); @@ -1164,12 +1159,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { Map<String, Object> params = configurations.get(0).getParams(); - if (mainConfig.isMaskPassword()) { - params.put(ActiveMQDefaultConfiguration.getPropMaskPassword(), mainConfig.isMaskPassword()); + params.put(ActiveMQDefaultConfiguration.getPropMaskPassword(), mainConfig.isMaskPassword()); - if (mainConfig.getPasswordCodec() != null) { - params.put(ActiveMQDefaultConfiguration.getPropPasswordCodec(), mainConfig.getPasswordCodec()); - } + if (mainConfig.getPasswordCodec() != null) { + params.put(ActiveMQDefaultConfiguration.getPropPasswordCodec(), mainConfig.getPasswordCodec()); } return configurations.get(0); @@ -1187,12 +1180,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { Map<String, Object> params = configurations.get(0).getParams(); - if (mainConfig.isMaskPassword()) { - params.put(ActiveMQDefaultConfiguration.getPropMaskPassword(), mainConfig.isMaskPassword()); + params.put(ActiveMQDefaultConfiguration.getPropMaskPassword(), mainConfig.isMaskPassword()); - if (mainConfig.getPasswordCodec() != null) { - params.put(ActiveMQDefaultConfiguration.getPropPasswordCodec(), mainConfig.getPasswordCodec()); - } + if (mainConfig.getPasswordCodec() != null) { + params.put(ActiveMQDefaultConfiguration.getPropPasswordCodec(), mainConfig.getPasswordCodec()); } return configurations.get(0); @@ -1720,9 +1711,6 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { NodeList clusterPassNodes = brNode.getElementsByTagName("password"); String password = null; - boolean maskPassword = mainConfig.isMaskPassword(); - - SensitiveDataCodec<String> codec = null; if (clusterPassNodes.getLength() > 0) { Node passNode = clusterPassNodes.item(0); @@ -1730,10 +1718,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil { } if (password != null) { - if (maskPassword) { - codec = PasswordMaskingUtil.getCodec(mainConfig.getPasswordCodec()); - password = codec.decode(password); - } + password = PasswordMaskingUtil.resolveMask(mainConfig.isMaskPassword(), password, mainConfig.getPasswordCodec()); } else { password = ActiveMQDefaultConfiguration.getDefaultClusterPassword(); } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java ---------------------------------------------------------------------- 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 a1ba4e7..e24f4f6 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 @@ -58,6 +58,7 @@ import java.util.Queue; import java.util.Set; import org.apache.activemq.artemis.core.server.ActiveMQServerLogger; +import org.apache.activemq.artemis.utils.PasswordMaskingUtil; import org.jboss.logging.Logger; public class LDAPLoginModule implements LoginModule { @@ -83,6 +84,8 @@ public class LDAPLoginModule implements LoginModule { private static final String SASL_LOGIN_CONFIG_SCOPE = "saslLoginConfigScope"; private static final String AUTHENTICATE_USER = "authenticateUser"; private static final String REFERRAL = "referral"; + private static final String MASK_PASSWORD = "maskPassword"; + private static final String PASSWORD_CODEC = "passwordCodec"; protected DirContext context; @@ -97,6 +100,8 @@ public class LDAPLoginModule implements LoginModule { private boolean isRoleAttributeSet = false; private String roleAttributeName = null; + private String codecClass = null; + @Override public void initialize(Subject subject, CallbackHandler callbackHandler, @@ -105,12 +110,38 @@ public class LDAPLoginModule implements LoginModule { this.subject = subject; this.handler = callbackHandler; - config = new LDAPLoginProperty[]{new LDAPLoginProperty(INITIAL_CONTEXT_FACTORY, (String) options.get(INITIAL_CONTEXT_FACTORY)), new LDAPLoginProperty(CONNECTION_URL, (String) options.get(CONNECTION_URL)), new LDAPLoginProperty(CONNECTION_USERNAME, (String) options.get(CONNECTION_USERNAME)), new LDAPLoginProperty(CONNECTION_PASSWORD, (String) options.get(CONNECTION_PASSWORD)), new LDAPLoginProperty(CONNECTION_PROTOCOL, (String) options.get(CONNECTION_PROTOCOL)), new LDAPLoginProperty(AUTHENTICATION, (String) options.get(AUTHENTICATION)), new LDAPLoginProperty(USER_BASE, (String) options.get(USER_BASE)), new LDAPLoginProperty(USER_SEARCH_MATCHING, (String) options.get(USER_SEARCH_MATCHING)), new LDAPLoginProperty(USER_SEARCH_SUBTREE, (String) options.get(USER_SEARCH_SUBTREE)), new LDAPLoginProperty(ROLE_BASE, (String) options.get(ROLE_BASE)), new LDAPLoginProperty(ROLE_NAME, (String) options.get(ROLE_NAME)), new LDAPLoginProperty(ROLE_SEARCH_MATCHING, (String) options.get(ROLE_S EARCH_MATCHING)), new LDAPLoginProperty(ROLE_SEARCH_SUBTREE, (String) options.get(ROLE_SEARCH_SUBTREE)), new LDAPLoginProperty(USER_ROLE_NAME, (String) options.get(USER_ROLE_NAME)), new LDAPLoginProperty(EXPAND_ROLES, (String) options.get(EXPAND_ROLES)), new LDAPLoginProperty(EXPAND_ROLES_MATCHING, (String) options.get(EXPAND_ROLES_MATCHING)), new LDAPLoginProperty(REFERRAL, (String) options.get(REFERRAL))}; + config = new LDAPLoginProperty[]{new LDAPLoginProperty(INITIAL_CONTEXT_FACTORY, (String) options.get(INITIAL_CONTEXT_FACTORY)), + new LDAPLoginProperty(CONNECTION_URL, (String) options.get(CONNECTION_URL)), + new LDAPLoginProperty(CONNECTION_USERNAME, (String) options.get(CONNECTION_USERNAME)), + new LDAPLoginProperty(CONNECTION_PASSWORD, (String) options.get(CONNECTION_PASSWORD)), + new LDAPLoginProperty(CONNECTION_PROTOCOL, (String) options.get(CONNECTION_PROTOCOL)), + new LDAPLoginProperty(AUTHENTICATION, (String) options.get(AUTHENTICATION)), + new LDAPLoginProperty(USER_BASE, (String) options.get(USER_BASE)), + new LDAPLoginProperty(USER_SEARCH_MATCHING, (String) options.get(USER_SEARCH_MATCHING)), + new LDAPLoginProperty(USER_SEARCH_SUBTREE, (String) options.get(USER_SEARCH_SUBTREE)), + new LDAPLoginProperty(ROLE_BASE, (String) options.get(ROLE_BASE)), + new LDAPLoginProperty(ROLE_NAME, (String) options.get(ROLE_NAME)), + new LDAPLoginProperty(ROLE_SEARCH_MATCHING, (String) options.get(ROLE_SEARCH_MATCHING)), + new LDAPLoginProperty(ROLE_SEARCH_SUBTREE, (String) options.get(ROLE_SEARCH_SUBTREE)), + new LDAPLoginProperty(USER_ROLE_NAME, (String) options.get(USER_ROLE_NAME)), + new LDAPLoginProperty(EXPAND_ROLES, (String) options.get(EXPAND_ROLES)), + new LDAPLoginProperty(EXPAND_ROLES_MATCHING, (String) options.get(EXPAND_ROLES_MATCHING)), + new LDAPLoginProperty(REFERRAL, (String) options.get(REFERRAL))}; + if (isLoginPropertySet(AUTHENTICATE_USER)) { authenticateUser = Boolean.valueOf(getLDAPPropertyValue(AUTHENTICATE_USER)); } isRoleAttributeSet = isLoginPropertySet(ROLE_NAME); roleAttributeName = getLDAPPropertyValue(ROLE_NAME); + codecClass = (String) options.get(PASSWORD_CODEC); + } + + private String getPlainPassword(String password) { + try { + return PasswordMaskingUtil.resolveMask(null, password, codecClass); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to decode password", e); + } } @Override @@ -511,7 +542,7 @@ public class LDAPLoginModule implements LoginModule { context.removeFromEnvironment(Context.SECURITY_PRINCIPAL); } if (isLoginPropertySet(CONNECTION_PASSWORD)) { - context.addToEnvironment(Context.SECURITY_CREDENTIALS, getLDAPPropertyValue(CONNECTION_PASSWORD)); + context.addToEnvironment(Context.SECURITY_CREDENTIALS, getPlainPassword(getLDAPPropertyValue(CONNECTION_PASSWORD))); } else { context.removeFromEnvironment(Context.SECURITY_CREDENTIALS); } @@ -575,7 +606,7 @@ public class LDAPLoginModule implements LoginModule { } if (isLoginPropertySet(CONNECTION_PASSWORD)) { - env.put(Context.SECURITY_CREDENTIALS, getLDAPPropertyValue(CONNECTION_PASSWORD)); + env.put(Context.SECURITY_CREDENTIALS, getPlainPassword(getLDAPPropertyValue(CONNECTION_PASSWORD))); } else { throw new NamingException("Empty password is not allowed"); } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/main/java/org/apache/activemq/artemis/utils/XMLConfigurationUtil.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/utils/XMLConfigurationUtil.java b/artemis-server/src/main/java/org/apache/activemq/artemis/utils/XMLConfigurationUtil.java index 7ce5280..29cfef9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/utils/XMLConfigurationUtil.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/utils/XMLConfigurationUtil.java @@ -116,7 +116,7 @@ public class XMLConfigurationUtil { return getTextBytesAsLongBytes(e, name, def, validator).intValue(); } - public static final Boolean getBoolean(final Element e, final String name, final boolean def) { + public static final Boolean getBoolean(final Element e, final String name, final Boolean def) { NodeList nl = e.getElementsByTagName(name); if (nl.getLength() > 0) { return XMLUtil.parseBoolean(nl.item(0)); http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java index 94d64ec..564372d 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java @@ -19,10 +19,12 @@ package org.apache.activemq.artemis.core.config.impl; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; import org.apache.activemq.artemis.api.core.SimpleString; +import org.apache.activemq.artemis.core.config.BridgeConfiguration; import org.apache.activemq.artemis.core.config.Configuration; import org.apache.activemq.artemis.core.config.FileDeploymentManager; import org.apache.activemq.artemis.core.config.HAPolicyConfiguration; @@ -32,6 +34,7 @@ import org.apache.activemq.artemis.core.deployers.impl.FileConfigurationParser; import org.apache.activemq.artemis.core.server.ActiveMQServer; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec; +import org.apache.activemq.artemis.utils.PasswordMaskingUtil; import org.junit.Assert; import org.junit.Test; @@ -190,6 +193,85 @@ public class FileConfigurationParserTest extends ActiveMQTestBase { assertEquals("newpassword", config.getClusterPassword()); } + @Test + public void testParsingDefaultServerConfigWithENCMaskedPwd() throws Exception { + FileConfigurationParser parser = new FileConfigurationParser(); + + String configStr = firstPart + lastPart; + ByteArrayInputStream input = new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8)); + + Configuration config = parser.parseMainConfig(input); + + String clusterPassword = config.getClusterPassword(); + + assertEquals(ActiveMQDefaultConfiguration.getDefaultClusterPassword(), clusterPassword); + + //if we add cluster-password, it should be default plain text + String clusterPasswordPart = "<cluster-password>ENC(5aec0780b12bf225a13ab70c6c76bc8e)</cluster-password>"; + + configStr = firstPart + clusterPasswordPart + lastPart; + + config = parser.parseMainConfig(new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8))); + + assertEquals("helloworld", config.getClusterPassword()); + + //if we add mask, it should be able to decode correctly + DefaultSensitiveStringCodec codec = new DefaultSensitiveStringCodec(); + String mask = (String) codec.encode("helloworld"); + + clusterPasswordPart = "<cluster-password>" + PasswordMaskingUtil.wrap(mask) + "</cluster-password>"; + + configStr = firstPart + clusterPasswordPart + lastPart; + + config = parser.parseMainConfig(new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8))); + + assertEquals("helloworld", config.getClusterPassword()); + + //if we change key, it should be able to decode correctly + codec = new DefaultSensitiveStringCodec(); + Map<String, String> prop = new HashMap<>(); + prop.put("key", "newkey"); + codec.init(prop); + + mask = (String) codec.encode("newpassword"); + + clusterPasswordPart = "<cluster-password>" + PasswordMaskingUtil.wrap(mask) + "</cluster-password>"; + + String codecPart = "<password-codec>" + "org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec" + + ";key=newkey</password-codec>"; + + configStr = firstPart + clusterPasswordPart + codecPart + lastPart; + + config = parser.parseMainConfig(new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8))); + + assertEquals("newpassword", config.getClusterPassword()); + + configStr = firstPart + bridgePart + lastPart; + config = parser.parseMainConfig(new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8))); + + List<BridgeConfiguration> bridgeConfigs = config.getBridgeConfigurations(); + assertEquals(1, bridgeConfigs.size()); + + BridgeConfiguration bconfig = bridgeConfigs.get(0); + + assertEquals("helloworld", bconfig.getPassword()); + } + + private static String bridgePart = "<bridges>\n" + + " <bridge name=\"my-bridge\">\n" + + " <queue-name>sausage-factory</queue-name>\n" + + " <forwarding-address>mincing-machine</forwarding-address>\n" + + " <filter string=\"name='aardvark'\"/>\n" + + " <transformer-class-name>org.apache.activemq.artemis.jms.example.HatColourChangeTransformer</transformer-class-name>\n" + + " <reconnect-attempts>-1</reconnect-attempts>\n" + + " <user>bridge-user</user>" + + " <password>ENC(5aec0780b12bf225a13ab70c6c76bc8e)</password>" + + " <static-connectors>\n" + + " <connector-ref>remote-connector</connector-ref>\n" + + " </static-connectors>\n" + + " </bridge>\n" + + "</bridges>\n"; + private static String firstPart = "<core xmlns=\"urn:activemq:core\">" + "\n" + "<name>ActiveMQ.main.config</name>" + "\n" + "<log-delegate-factory-class-name>org.apache.activemq.artemis.integration.logging.Log4jLogDelegateFactory</log-delegate-factory-class-name>" + "\n" + http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleMaskPasswordTest.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleMaskPasswordTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleMaskPasswordTest.java new file mode 100644 index 0000000..cfb9934 --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleMaskPasswordTest.java @@ -0,0 +1,156 @@ +/* + * 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 org.apache.directory.server.annotations.CreateLdapServer; +import org.apache.directory.server.annotations.CreateTransport; +import org.apache.directory.server.core.annotations.ApplyLdifFiles; +import org.apache.directory.server.core.integ.AbstractLdapTestUnit; +import org.apache.directory.server.core.integ.FrameworkRunner; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +@RunWith(FrameworkRunner.class) +@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP", port = 1024)}) +@ApplyLdifFiles("test.ldif") +public class LDAPLoginModuleMaskPasswordTest extends AbstractLdapTestUnit { + + private final String loginConfigSysPropName = "java.security.auth.login.config"; + private String oldLoginConfig; + + @Before + public void setLoginConfigSysProperty() { + oldLoginConfig = System.getProperty(loginConfigSysPropName, null); + System.setProperty(loginConfigSysPropName, "src/test/resources/login.config"); + } + + @After + public void resetLoginConfigSysProperty() { + if (oldLoginConfig != null) { + System.setProperty(loginConfigSysPropName, oldLoginConfig); + } + } + + @Test + public void testLoginMaskedPassword() throws LoginException { + LoginContext context = new LoginContext("LDAPLoginMaskedPassword", callbacks -> { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + }); + context.login(); + context.logout(); + } + + @Test + public void testLoginMaskedPasswordUnauthenticated() throws LoginException { + LoginContext context = new LoginContext("LDAPLoginMaskedPassword", callbacks -> { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("nosecret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + }); + try { + context.login(); + } catch (FailedLoginException le) { + assertEquals(le.getMessage(), "Password does not match for user: first"); + return; + } + fail("Should have failed authenticating"); + } + + @Test + public void testLoginExternalCodec() throws LoginException { + LoginContext context = new LoginContext("LDAPLoginExternalPasswordCodec", callbacks -> { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + }); + + context.login(); + context.logout(); + } + + @Test + public void testLoginExternalCodec2() throws LoginException { + LoginContext context = new LoginContext("LDAPLoginExternalPasswordCodec2", callbacks -> { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + }); + + context.login(); + context.logout(); + } + + @Test + public void testLoginExternalCodecUnauthenticated() throws LoginException { + LoginContext context = new LoginContext("LDAPLoginExternalPasswordCodec", callbacks -> { + for (int i = 0; i < callbacks.length; i++) { + if (callbacks[i] instanceof NameCallback) { + ((NameCallback) callbacks[i]).setName("first"); + } else if (callbacks[i] instanceof PasswordCallback) { + ((PasswordCallback) callbacks[i]).setPassword("nosecret".toCharArray()); + } else { + throw new UnsupportedCallbackException(callbacks[i]); + } + } + }); + try { + context.login(); + } catch (FailedLoginException le) { + assertEquals(le.getMessage(), "Password does not match for user: first"); + return; + } + fail("Should have failed authenticating"); + } +} http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java ---------------------------------------------------------------------- diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java index 1ab5dba..eb6c9fa 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java @@ -77,6 +77,13 @@ public class PropertiesLoginModuleTest extends Assert { } @Test + public void testLoginMasked() throws LoginException { + LoginContext context = new LoginContext("PropertiesLogin", new UserPassHandler("third", "helloworld")); + context.login(); + context.logout(); + } + + @Test public void testLoginReload() throws Exception { File targetPropDir = new File("target/loginReloadTest"); File usersFile = new File(targetPropDir, "users.properties"); http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/test/resources/login.config ---------------------------------------------------------------------- diff --git a/artemis-server/src/test/resources/login.config b/artemis-server/src/test/resources/login.config index 997bfe5..8e531ca 100644 --- a/artemis-server/src/test/resources/login.config +++ b/artemis-server/src/test/resources/login.config @@ -125,3 +125,62 @@ OpenLdapConfiguration { roleSearchSubtree=true ; }; + +LDAPLoginMaskedPassword { + org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required + debug=true + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:1024" + connectionUsername="uid=admin,ou=system" + connectionPassword="ENC(-41e444c3ed07d6dd)" + connectionProtocol=s + authentication=simple + userBase="ou=system" + userSearchMatching="(uid={0})" + userSearchSubtree=false + roleBase="ou=system" + roleName=cn + roleSearchMatching="(member=uid={1},ou=system)" + roleSearchSubtree=false + ; +}; + +LDAPLoginExternalPasswordCodec { + org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required + debug=true + passwordCodec="org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;key=helloworld" + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:1024" + connectionUsername="uid=admin,ou=system" + connectionPassword="ENC(-170b9ef34d79ed12)" + connectionProtocol=s + authentication=simple + userBase="ou=system" + userSearchMatching="(uid={0})" + userSearchSubtree=false + roleBase="ou=system" + roleName=dummyRoleName + roleSearchMatching="(uid={1})" + roleSearchSubtree=false + ; +}; + +LDAPLoginExternalPasswordCodec2 { + org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required + debug=true + passwordCodec="org.apache.activemq.artemis.utils.MaskPasswordResolvingTest$SimplePasswordCodec" + initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory + connectionURL="ldap://localhost:1024" + connectionUsername="uid=admin,ou=system" + connectionPassword="ENC(momsword)" + connectionProtocol=s + authentication=simple + userBase="ou=system" + userSearchMatching="(uid={0})" + userSearchSubtree=false + roleBase="ou=system" + roleName=dummyRoleName + roleSearchMatching="(uid={1})" + roleSearchSubtree=false + ; +}; http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-server/src/test/resources/users.properties ---------------------------------------------------------------------- diff --git a/artemis-server/src/test/resources/users.properties b/artemis-server/src/test/resources/users.properties index 1087b0b..c1a6b47 100644 --- a/artemis-server/src/test/resources/users.properties +++ b/artemis-server/src/test/resources/users.properties @@ -17,3 +17,4 @@ first=secret second=password +third=ENC(1024:439F45267508BB4F02150B9AAFB8D774AB7FCDDE9B19D096F318787487F7BD17:78818E53AEE8AF26A34A38C1498D1CDA5636861D2FE9804FEDE3656D0BC05696191A027F095DF109EC8F6385FAE9971915449EC808945A0F5907B29D5F9D44B7) \ No newline at end of file http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-web/pom.xml ---------------------------------------------------------------------- diff --git a/artemis-web/pom.xml b/artemis-web/pom.xml index a9be5c0..4edc47a 100644 --- a/artemis-web/pom.xml +++ b/artemis-web/pom.xml @@ -69,6 +69,13 @@ <version>${project.version}</version> </dependency> <dependency> + <groupId>org.apache.activemq</groupId> + <artifactId>artemis-commons</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + <dependency> <groupId>io.netty</groupId> <artifactId>netty-buffer</artifactId> </dependency> http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java ---------------------------------------------------------------------- diff --git a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java index 9964c71..d85d621 100644 --- a/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java +++ b/artemis-web/src/main/java/org/apache/activemq/artemis/component/WebServerComponent.java @@ -68,12 +68,12 @@ public class WebServerComponent implements ExternalComponent { if ("https".equals(scheme)) { SslContextFactory sslFactory = new SslContextFactory(); sslFactory.setKeyStorePath(webServerConfig.keyStorePath == null ? artemisInstance + "/etc/keystore.jks" : webServerConfig.keyStorePath); - sslFactory.setKeyStorePassword(webServerConfig.keyStorePassword == null ? "password" : webServerConfig.keyStorePassword); + sslFactory.setKeyStorePassword(webServerConfig.getKeyStorePassword() == null ? "password" : webServerConfig.getKeyStorePassword()); if (webServerConfig.clientAuth != null) { sslFactory.setNeedClientAuth(webServerConfig.clientAuth); if (webServerConfig.clientAuth) { sslFactory.setTrustStorePath(webServerConfig.trustStorePath); - sslFactory.setTrustStorePassword(webServerConfig.trustStorePassword); + sslFactory.setTrustStorePassword(webServerConfig.getTrustStorePassword()); } } http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java ---------------------------------------------------------------------- diff --git a/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java b/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java index 00691ed..1178147 100644 --- a/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java +++ b/artemis-web/src/test/java/org/apache/activemq/cli/test/WebServerComponentTest.java @@ -18,6 +18,7 @@ package org.apache.activemq.cli.test; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; +import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -43,9 +44,11 @@ import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.ssl.SslHandler; import io.netty.util.CharsetUtil; +import org.apache.activemq.artemis.cli.factory.xml.XmlBrokerFactoryHandler; import org.apache.activemq.artemis.component.WebServerComponent; import org.apache.activemq.artemis.core.remoting.impl.ssl.SSLSupport; import org.apache.activemq.artemis.core.server.ActiveMQComponent; +import org.apache.activemq.artemis.dto.BrokerDTO; import org.apache.activemq.artemis.dto.WebServerDTO; import org.junit.After; import org.junit.Assert; @@ -163,7 +166,7 @@ public class WebServerComponentTest extends Assert { webServerDTO.bind = "https://localhost:0"; webServerDTO.path = "webapps"; webServerDTO.keyStorePath = "./src/test/resources/server.keystore"; - webServerDTO.keyStorePassword = "password"; + webServerDTO.setKeyStorePassword("password"); WebServerComponent webServerComponent = new WebServerComponent(); Assert.assertFalse(webServerComponent.isStarted()); @@ -174,7 +177,7 @@ public class WebServerComponentTest extends Assert { // Make the connection attempt. String keyStoreProvider = "JKS"; - SSLContext context = SSLSupport.createContext(keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.keyStorePassword, keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.keyStorePassword); + SSLContext context = SSLSupport.createContext(keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.getKeyStorePassword(), keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.getKeyStorePassword()); SSLEngine engine = context.createSSLEngine(); engine.setUseClientMode(true); @@ -215,10 +218,10 @@ public class WebServerComponentTest extends Assert { webServerDTO.bind = "https://localhost:0"; webServerDTO.path = "webapps"; webServerDTO.keyStorePath = "./src/test/resources/server.keystore"; - webServerDTO.keyStorePassword = "password"; + webServerDTO.setKeyStorePassword("password"); webServerDTO.clientAuth = true; webServerDTO.trustStorePath = "./src/test/resources/server.keystore"; - webServerDTO.trustStorePassword = "password"; + webServerDTO.setTrustStorePassword("password"); WebServerComponent webServerComponent = new WebServerComponent(); Assert.assertFalse(webServerComponent.isStarted()); @@ -229,7 +232,7 @@ public class WebServerComponentTest extends Assert { // Make the connection attempt. String keyStoreProvider = "JKS"; - SSLContext context = SSLSupport.createContext(keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.keyStorePassword, keyStoreProvider, webServerDTO.trustStorePath, webServerDTO.trustStorePassword); + SSLContext context = SSLSupport.createContext(keyStoreProvider, webServerDTO.keyStorePath, webServerDTO.getKeyStorePassword(), keyStoreProvider, webServerDTO.trustStorePath, webServerDTO.getTrustStorePassword()); SSLEngine engine = context.createSSLEngine(); engine.setUseClientMode(true); @@ -264,6 +267,44 @@ public class WebServerComponentTest extends Assert { Assert.assertFalse(webServerComponent.isStarted()); } + @Test + public void testDefaultMaskPasswords() throws Exception { + File bootstrap = new File("./target/test-classes/bootstrap_web.xml"); + File brokerHome = new File("./target"); + XmlBrokerFactoryHandler xmlHandler = new XmlBrokerFactoryHandler(); + BrokerDTO broker = xmlHandler.createBroker(bootstrap.toURI(), brokerHome.getAbsolutePath(), brokerHome.getAbsolutePath(), brokerHome.toURI()); + assertNotNull(broker.web); + assertNull(broker.web.passwordCodec); + } + + @Test + public void testMaskPasswords() throws Exception { + final String keyPassword = "keypass"; + final String trustPassword = "trustpass"; + File bootstrap = new File("./target/test-classes/bootstrap_secure_web.xml"); + File brokerHome = new File("./target"); + XmlBrokerFactoryHandler xmlHandler = new XmlBrokerFactoryHandler(); + BrokerDTO broker = xmlHandler.createBroker(bootstrap.toURI(), brokerHome.getAbsolutePath(), brokerHome.getAbsolutePath(), brokerHome.toURI()); + assertNotNull(broker.web); + assertEquals(keyPassword, broker.web.getKeyStorePassword()); + assertEquals(trustPassword, broker.web.getTrustStorePassword()); + } + + @Test + public void testMaskPasswordCodec() throws Exception { + final String keyPassword = "keypass"; + final String trustPassword = "trustpass"; + File bootstrap = new File("./target/test-classes/bootstrap_web_codec.xml"); + File brokerHome = new File("./target"); + XmlBrokerFactoryHandler xmlHandler = new XmlBrokerFactoryHandler(); + BrokerDTO broker = xmlHandler.createBroker(bootstrap.toURI(), brokerHome.getAbsolutePath(), brokerHome.getAbsolutePath(), brokerHome.toURI()); + assertNotNull(broker.web); + assertNotNull("password codec not picked up!", broker.web.passwordCodec); + + assertEquals(keyPassword, broker.web.getKeyStorePassword()); + assertEquals(trustPassword, broker.web.getTrustStorePassword()); + } + class ClientHandler extends SimpleChannelInboundHandler<HttpObject> { private CountDownLatch latch; http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-web/src/test/resources/bootstrap_secure_web.xml ---------------------------------------------------------------------- diff --git a/artemis-web/src/test/resources/bootstrap_secure_web.xml b/artemis-web/src/test/resources/bootstrap_secure_web.xml new file mode 100644 index 0000000..affa9ab --- /dev/null +++ b/artemis-web/src/test/resources/bootstrap_secure_web.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ~ 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. + --> + +<broker xmlns="http://activemq.org/schema"> + + <jaas-security domain="activemq"/> + + <!-- artemis.URI.instance is parsed from artemis.instance by the CLI startup. + This is to avoid situations where you could have spaces or special characters on this URI --> + <server configuration="${artemis.URI.instance}/etc/broker.xml"/> + + <!-- The web server is only bound to localhost by default --> + <web bind="https://localhost:8443" path="web" keyStorePassword="ENC(-5a2376c61c668aaf)" trustStorePassword="ENC(3d617352d12839eb71208edf41d66b34)"> + <app url="activemq-branding" war="activemq-branding.war"/> + </web> + + +</broker> + http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-web/src/test/resources/bootstrap_web.xml ---------------------------------------------------------------------- diff --git a/artemis-web/src/test/resources/bootstrap_web.xml b/artemis-web/src/test/resources/bootstrap_web.xml new file mode 100644 index 0000000..847a869 --- /dev/null +++ b/artemis-web/src/test/resources/bootstrap_web.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ~ 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. + --> + +<broker xmlns="http://activemq.org/schema"> + + <jaas-security domain="activemq"/> + + <!-- artemis.URI.instance is parsed from artemis.instance by the CLI startup. + This is to avoid situations where you could have spaces or special characters on this URI --> + <server configuration="${artemis.URI.instance}/etc/broker.xml"/> + + <!-- The web server is only bound to localhost by default --> + <web bind="http://localhost:8161" path="web"> + <app url="activemq-branding" war="activemq-branding.war"/> + </web> + + +</broker> + http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/bb84f679/artemis-web/src/test/resources/bootstrap_web_codec.xml ---------------------------------------------------------------------- diff --git a/artemis-web/src/test/resources/bootstrap_web_codec.xml b/artemis-web/src/test/resources/bootstrap_web_codec.xml new file mode 100644 index 0000000..7b53515 --- /dev/null +++ b/artemis-web/src/test/resources/bootstrap_web_codec.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ~ 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. + --> + +<broker xmlns="http://activemq.org/schema"> + + <jaas-security domain="activemq"/> + + <!-- artemis.URI.instance is parsed from artemis.instance by the CLI startup. + This is to avoid situations where you could have spaces or special characters on this URI --> + <server configuration="${artemis.URI.instance}/etc/broker.xml"/> + + <!-- The web server is only bound to localhost by default --> + <web bind="https://localhost:8443" path="web" passwordCodec="org.apache.activemq.artemis.utils.MaskPasswordResolvingTest$SimplePasswordCodec" keyStorePassword="ENC(youneverknow)" trustStorePassword="ENC(youcanguess)"> + <app url="console" war="console.war"/> + </web> + + +</broker> +