activemq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From clebertsuco...@apache.org
Subject [1/5] activemq-artemis git commit: ARTEMIS-786 Store user's password in hash form by default - user passwords for PropertiesLoginModule stored using PBKDF2 algothrim by default - implements cli user command to help create and manage user/roles
Date Wed, 02 Nov 2016 19:53:14 GMT
Repository: activemq-artemis
Updated Branches:
  refs/heads/master 8b57516e1 -> 9f7fc8836


http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/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
new file mode 100644
index 0000000..81db051
--- /dev/null
+++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/SecureHashProcessor.java
@@ -0,0 +1,44 @@
+/*
+ * 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;
+
+/**
+ * Hash function
+ */
+public class SecureHashProcessor implements HashProcessor {
+
+   private static final String BEGIN_HASH = "ENC(";
+   private static final String END_HASH = ")";
+
+   private DefaultSensitiveStringCodec codec;
+
+   public SecureHashProcessor(DefaultSensitiveStringCodec codec) {
+      this.codec = codec;
+   }
+
+   @Override
+   public String hash(String plainText) throws Exception {
+      return BEGIN_HASH + codec.encode(plainText) + END_HASH;
+   }
+
+   @Override
+   //storedValue must take form of ENC(...)
+   public boolean compare(char[] inputValue, String storedValue) {
+      String storedHash = storedValue.substring(4, storedValue.length() - 2);
+      return codec.verify(inputValue, storedHash);
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/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 b1bfd2b..d585976 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
@@ -29,5 +29,7 @@ public interface SensitiveDataCodec<T> {
 
    T decode(Object mask) throws Exception;
 
-   void init(Map<String, String> params);
+   T encode(Object secret) throws Exception;
+
+   void init(Map<String, String> params) throws Exception;
 }

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/DefaultSensitiveStringCodecTest.java
----------------------------------------------------------------------
diff --git a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/DefaultSensitiveStringCodecTest.java
b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/DefaultSensitiveStringCodecTest.java
new file mode 100644
index 0000000..393558d
--- /dev/null
+++ b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/DefaultSensitiveStringCodecTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class DefaultSensitiveStringCodecTest {
+
+   @Test
+   public void testDefaultAlgorithm() throws Exception {
+      SensitiveDataCodec<String> codec = PasswordMaskingUtil.getDefaultCodec();
+      assertTrue(codec instanceof DefaultSensitiveStringCodec);
+   }
+
+   @Test
+   public void testOnewayAlgorithm() throws Exception {
+      DefaultSensitiveStringCodec codec = new DefaultSensitiveStringCodec();
+      Map<String, String> params = new HashMap<>();
+      params.put(DefaultSensitiveStringCodec.ALGORITHM, DefaultSensitiveStringCodec.ONE_WAY);
+      codec.init(params);
+
+      String plainText = "some_password";
+      String maskedText = codec.encode(plainText);
+      System.out.println("encoded value: " + maskedText);
+
+      //one way can't decode
+      try {
+         codec.decode(maskedText);
+         fail("one way algorithm can't decode");
+      } catch (IllegalArgumentException expected) {
+      }
+
+      assertTrue(codec.verify(plainText.toCharArray(), maskedText));
+
+      String otherPassword = "some_other_password";
+      assertFalse(codec.verify(otherPassword.toCharArray(), maskedText));
+   }
+
+   @Test
+   public void testTwowayAlgorithm() throws Exception {
+      DefaultSensitiveStringCodec codec = new DefaultSensitiveStringCodec();
+      Map<String, String> params = new HashMap<>();
+      params.put(DefaultSensitiveStringCodec.ALGORITHM, DefaultSensitiveStringCodec.TWO_WAY);
+      codec.init(params);
+
+      String plainText = "some_password";
+      String maskedText = codec.encode(plainText);
+      System.out.println("encoded value: " + maskedText);
+
+      String decoded = codec.decode(maskedText);
+      System.out.println("encoded value: " + maskedText);
+
+      assertEquals("decoded result not match: " + decoded, decoded, plainText);
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/HashProcessorTest.java
----------------------------------------------------------------------
diff --git a/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/HashProcessorTest.java
b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/HashProcessorTest.java
new file mode 100644
index 0000000..83199b5
--- /dev/null
+++ b/artemis-commons/src/test/java/org/apache/activemq/artemis/utils/HashProcessorTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+
+@RunWith(Parameterized.class)
+public class HashProcessorTest {
+
+   private static final String USER1_PASSWORD = "password";
+   private static final String USER1_HASHED_PASSWORD = "ENC(1024:973A466A489ABFDED3D4B3D181DC77F410F2FC6E87432809A46B72B294147D76:C999ECA8A85387E1FFB14E4FE5CECD17948BA80BA04318A9BE4C3E34B7FE2925F43AB6BC9DFE0D9855DA67439AEEB9850351BC4D5D3AEC6A6903C42B8EB4ED1E)";
+
+   private static final String USER2_PASSWORD = "manager";
+   private static final String USER2_HASHED_PASSWORD = "ENC(1024:48018CDB1B5925DA2CC51DBD6F7E8C5FF156C22C03C6C69720C56F8BE76A1D48:0A0F68C2C01F46D347C6C51D641291A4608EDA50A873ED122909D9134B7A757C14176F0C033F0BD3CE35B3C373D5B652650CDE5FFBBB0F286D4495CEFEEDB166)";
+
+   private static final String USER3_PASSWORD = "artemis000";
+
+   @Parameterized.Parameters(name = "{index}: testing password {0}")
+   public static Collection<Object[]> data() {
+      return Arrays.asList(new Object[][] {
+         {USER1_PASSWORD, USER1_HASHED_PASSWORD, true},
+         {USER2_PASSWORD, USER2_HASHED_PASSWORD, true},
+         {USER3_PASSWORD, USER3_PASSWORD, true},
+         {USER1_PASSWORD, USER2_PASSWORD, false},
+         {USER3_PASSWORD, USER2_HASHED_PASSWORD, false}
+      });
+   }
+
+   private String password;
+   private String storedPassword;
+   private boolean match;
+
+   public HashProcessorTest(String password, String storedPassword, boolean match) {
+      this.password = password;
+      this.storedPassword = storedPassword;
+      this.match = match;
+   }
+
+   @Test
+   public void testPasswordVerification() throws Exception {
+      HashProcessor processor = PasswordMaskingUtil.getHashProcessor(storedPassword);
+      boolean result = processor.compare(password.toCharArray(), storedPassword);
+      assertEquals(match, result);
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-distribution/src/main/assembly/dep.xml
----------------------------------------------------------------------
diff --git a/artemis-distribution/src/main/assembly/dep.xml b/artemis-distribution/src/main/assembly/dep.xml
index d21f1e0..aa3635e 100644
--- a/artemis-distribution/src/main/assembly/dep.xml
+++ b/artemis-distribution/src/main/assembly/dep.xml
@@ -89,6 +89,8 @@
             <include>commons-beanutils:commons-beanutils</include>
             <include>commons-logging:commons-logging</include>
             <include>commons-collections:commons-collections</include>
+            <include>org.apache.commons:commons-configuration2</include>
+            <include>org.apache.commons:commons-lang3</include>
             <include>org.fusesource.hawtbuf:hawtbuf</include>
             <include>org.jgroups:jgroups</include>
             <include>org.apache.geronimo.specs:geronimo-json_1.0_spec</include>

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java
b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java
index d120a98..957bb8a 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java
@@ -32,14 +32,16 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 
+import org.apache.activemq.artemis.utils.HashProcessor;
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
 import org.jboss.logging.Logger;
 
 public class PropertiesLoginModule extends PropertiesLoader implements LoginModule {
 
    private static final Logger logger = Logger.getLogger(PropertiesLoginModule.class);
 
-   private static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.user";
-   private static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.role";
+   public static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.user";
+   public static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.properties.role";
 
    private Subject subject;
    private CallbackHandler callbackHandler;
@@ -49,6 +51,7 @@ public class PropertiesLoginModule extends PropertiesLoader implements LoginModu
    private String user;
    private final Set<Principal> principals = new HashSet<>();
    private boolean loginSucceeded;
+   private HashProcessor hashProcessor;
 
    @Override
    public void initialize(Subject subject,
@@ -90,10 +93,21 @@ public class PropertiesLoginModule extends PropertiesLoader implements
LoginModu
       if (password == null) {
          throw new FailedLoginException("User does exist");
       }
-      if (!password.equals(new String(tmpPassword))) {
-         throw new FailedLoginException("Password does not match");
+
+      //password is hashed
+      try {
+         hashProcessor = PasswordMaskingUtil.getHashProcessor(password);
+
+         if (!hashProcessor.compare(tmpPassword, password)) {
+            throw new FailedLoginException("Password does not match");
+         }
+         loginSucceeded = true;
+      } catch (Exception e) {
+         if (debug) {
+            logger.debug("Exception getting a hash processor", e);
+         }
+         throw new FailedLoginException("Failed to get hash processor");
       }
-      loginSucceeded = true;
 
       if (debug) {
          logger.debug("login " + user);

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/cd7b8389/docs/user-manual/en/configuration-index.md
----------------------------------------------------------------------
diff --git a/docs/user-manual/en/configuration-index.md b/docs/user-manual/en/configuration-index.md
index 65ef931..240d6db 100644
--- a/docs/user-manual/en/configuration-index.md
+++ b/docs/user-manual/en/configuration-index.md
@@ -396,29 +396,14 @@ will have to be in masked form.
 ### Masking passwords in artemis-users.properties
 
 Apache ActiveMQ Artemis's built-in security manager uses plain properties files
-where the user passwords are specified in plaintext forms by default. To
-mask those parameters the following two properties need to be set
-in the 'bootstrap.xml' file.
+where the user passwords are specified in hash forms by default. 
 
-`mask-password` -- If set to "true" all the passwords are masked.
-Default is false.
-
-`password-codec` -- Class name and its parameters for the Decoder used
-to decode the masked password. Ignored if `mask-password` is false. The
-format of this property is a full qualified class name optionally
-followed by key/value pairs. It is the same format as that for JMS
-Bridges. Example:
+Please use Artemis CLI command to add a password. For example
 
-```xml
-<mask-password>true</mask-password>
-<password-codec>org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;key=hello
world</password-codec>
+```sh
+    ./artemis user add --username guest --password guest --role admin
 ```
 
-When so configured, the Apache ActiveMQ Artemis security manager will initialize a
-DefaultSensitiveStringCodec with the parameters "key"-\>"hello world",
-then use it to decode all the masked passwords in this configuration
-file.
-
 ### Choosing a decoder for password masking
 
 As described in the previous sections, all password masking requires a


Mime
View raw message