activemq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From clebertsuco...@apache.org
Subject [1/2] activemq-artemis git commit: ARTEMIS-1740: Add support for regex based certificate authentication
Date Thu, 12 Apr 2018 16:57:17 GMT
Repository: activemq-artemis
Updated Branches:
  refs/heads/master 64f07518a -> 5535af1a8


ARTEMIS-1740: Add support for regex based certificate authentication


Project: http://git-wip-us.apache.org/repos/asf/activemq-artemis/repo
Commit: http://git-wip-us.apache.org/repos/asf/activemq-artemis/commit/1e81361a
Tree: http://git-wip-us.apache.org/repos/asf/activemq-artemis/tree/1e81361a
Diff: http://git-wip-us.apache.org/repos/asf/activemq-artemis/diff/1e81361a

Branch: refs/heads/master
Commit: 1e81361a88a2dd333d7f5c7f926dde515e5b7ac5
Parents: 64f0751
Author: Lionel Cons <lionel.cons@cern.ch>
Authored: Wed Apr 11 08:59:24 2018 +0200
Committer: Clebert Suconic <clebertsuconic@apache.org>
Committed: Thu Apr 12 12:55:20 2018 -0400

----------------------------------------------------------------------
 .../security/jaas/ReloadableProperties.java     | 32 +++++++++++++++++++-
 .../jaas/TextFileCertificateLoginModule.java    | 20 ++++++++++--
 .../TextFileCertificateLoginModuleTest.java     |  6 ++++
 .../test/resources/cert-users-REGEXP.properties | 18 +++++++++++
 docs/user-manual/en/security.md                 |  7 +++--
 .../integration/security/SecurityTest.java      | 18 ++++++++---
 .../src/test/resources/cert-regexps.properties  | 19 ++++++++++++
 .../src/test/resources/login.config             |  7 +++++
 8 files changed, 118 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/1e81361a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ReloadableProperties.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ReloadableProperties.java
b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ReloadableProperties.java
index bed7d5f..947c6d1 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ReloadableProperties.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ReloadableProperties.java
@@ -24,6 +24,8 @@ import java.util.HashSet;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 
 import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
 import org.jboss.logging.Logger;
@@ -35,6 +37,7 @@ public class ReloadableProperties {
    private Properties props = new Properties();
    private Map<String, String> invertedProps;
    private Map<String, Set<String>> invertedValueProps;
+   private Map<String, Pattern> regexpProps;
    private long reloadTime = -1;
    private final PropertiesLoader.FileNameKey key;
 
@@ -53,6 +56,7 @@ public class ReloadableProperties {
             load(key.file(), props);
             invertedProps = null;
             invertedValueProps = null;
+            regexpProps = null;
             if (key.isDebug()) {
                logger.debug("Load of: " + key);
             }
@@ -71,7 +75,10 @@ public class ReloadableProperties {
       if (invertedProps == null) {
          invertedProps = new HashMap<>(props.size());
          for (Map.Entry<Object, Object> val : props.entrySet()) {
-            invertedProps.put((String) val.getValue(), (String) val.getKey());
+            String str = (String) val.getValue();
+            if (!looksLikeRegexp(str)) {
+               invertedProps.put(str, (String) val.getKey());
+            }
          }
       }
       return invertedProps;
@@ -95,6 +102,24 @@ public class ReloadableProperties {
       return invertedValueProps;
    }
 
+   public synchronized Map<String, Pattern> regexpPropertiesMap() {
+      if (regexpProps == null) {
+         regexpProps = new HashMap<>(props.size());
+         for (Map.Entry<Object, Object> val : props.entrySet()) {
+            String str = (String) val.getValue();
+            if (looksLikeRegexp(str)) {
+               try {
+                  Pattern p = Pattern.compile(str.substring(1, str.length() - 1));
+                  regexpProps.put((String) val.getKey(), p);
+               } catch (PatternSyntaxException e) {
+                  ActiveMQServerLogger.LOGGER.warn("Ignoring invalid regexp: " + str);
+               }
+            }
+         }
+      }
+      return regexpProps;
+   }
+
    private void load(final File source, Properties props) throws IOException {
       try (FileInputStream in = new FileInputStream(source)) {
          props.load(in);
@@ -115,4 +140,9 @@ public class ReloadableProperties {
       return key.file.lastModified() > reloadTime;
    }
 
+   private boolean looksLikeRegexp(String str) {
+      int len = str.length();
+      return len > 2 && str.charAt(0) == '/' && str.charAt(len - 1) ==
'/';
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/1e81361a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/TextFileCertificateLoginModule.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/TextFileCertificateLoginModule.java
b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/TextFileCertificateLoginModule.java
index ca103cd..2b0f45c 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/TextFileCertificateLoginModule.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/TextFileCertificateLoginModule.java
@@ -23,6 +23,7 @@ import javax.security.cert.X509Certificate;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 /**
  * A LoginModule allowing for SSL certificate based authentication based on
@@ -41,6 +42,7 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule
{
    private static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.role";
 
    private Map<String, Set<String>> rolesByUser;
+   private Map<String, Pattern> regexpByUser;
    private Map<String, String> usersByDn;
 
    /**
@@ -53,6 +55,7 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule
{
                           Map<String, ?> options) {
       super.initialize(subject, callbackHandler, sharedState, options);
       usersByDn = load(USER_FILE_PROP_NAME, "", options).invertedPropertiesMap();
+      regexpByUser = load(USER_FILE_PROP_NAME, "", options).regexpPropertiesMap();
       rolesByUser = load(ROLE_FILE_PROP_NAME, "", options).invertedPropertiesValuesMap();
    }
 
@@ -71,8 +74,8 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule
{
       if (certs == null) {
          throw new LoginException("Client certificates not found. Cannot authenticate.");
       }
-
-      return usersByDn.get(getDistinguishedName(certs));
+      String dn = getDistinguishedName(certs);
+      return usersByDn.containsKey(dn) ? usersByDn.get(dn) : getUserByRegexp(dn);
    }
 
    /**
@@ -92,4 +95,17 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule
{
 
       return userRoles;
    }
+
+   private synchronized String getUserByRegexp(String dn) {
+      String name = null;
+      for (Map.Entry<String, Pattern> val : regexpByUser.entrySet()) {
+         if (val.getValue().matcher(dn).matches()) {
+            name = val.getKey();
+            break;
+         }
+      }
+      usersByDn.put(dn, name);
+      return name;
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/1e81361a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/TextFileCertificateLoginModuleTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/TextFileCertificateLoginModuleTest.java
b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/TextFileCertificateLoginModuleTest.java
index 2eebd06..957dace 100644
--- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/TextFileCertificateLoginModuleTest.java
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/TextFileCertificateLoginModuleTest.java
@@ -46,6 +46,7 @@ public class TextFileCertificateLoginModuleTest {
 
    private static final String CERT_USERS_FILE_SMALL = "cert-users-SMALL.properties";
    private static final String CERT_USERS_FILE_LARGE = "cert-users-LARGE.properties";
+   private static final String CERT_USERS_FILE_REGEXP = "cert-users-REGEXP.properties";
    private static final String CERT_GROUPS_FILE = "cert-roles.properties";
 
    private static final int NUMBER_SUBJECTS = 10;
@@ -88,6 +89,11 @@ public class TextFileCertificateLoginModuleTest {
       loginTest(CERT_USERS_FILE_LARGE, CERT_GROUPS_FILE);
    }
 
+   @Test
+   public void testLoginWithREGEXPUsersFile() throws Exception {
+      loginTest(CERT_USERS_FILE_REGEXP, CERT_GROUPS_FILE);
+   }
+
    private void loginTest(String usersFiles, String groupsFile) throws LoginException {
 
       HashMap<String, String> options = new HashMap<>();

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/1e81361a/artemis-server/src/test/resources/cert-users-REGEXP.properties
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/resources/cert-users-REGEXP.properties b/artemis-server/src/test/resources/cert-users-REGEXP.properties
new file mode 100644
index 0000000..22e67a6
--- /dev/null
+++ b/artemis-server/src/test/resources/cert-users-REGEXP.properties
@@ -0,0 +1,18 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+CNODD=/DN=TEST_USER_\\d*[13579]/
+CNEVEN=/DN=TEST_USER_\\d*[02468]/

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/1e81361a/docs/user-manual/en/security.md
----------------------------------------------------------------------
diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md
index ac0c76f..2f70701 100644
--- a/docs/user-manual/en/security.md
+++ b/docs/user-manual/en/security.md
@@ -615,12 +615,15 @@ login module. The options supported by this login module are as follows:
 -   `reload` - boolean flag; whether or not to reload the properties files when a modification
occurs; default is `false`
 
 In the context of the certificate login module, the `users.properties` file consists of a
list of properties of the form,
-`UserName=StringifiedSubjectDN`. For example, to define the users, system, user, and guest,
you could create a file like
-the following:
+`UserName=StringifiedSubjectDN` or `UserName=/SubjectDNRegExp/`. For example, to define the
users, `system`, `user` and
+`guest` as well as a `hosts` user matching several DNs, you could create a file like the
following:
 
     system=CN=system,O=Progress,C=US
     user=CN=humble user,O=Progress,C=US
     guest=CN=anon,O=Progress,C=DE
+    hosts=/CN=host\\d+\\.acme\\.com,O=Acme,C=UK/
+
+Note that the backslash character has to be escaped because it has a special treatment in
properties files.
 
 Each username is mapped to a subject DN, encoded as a string (where the string encoding is
specified by RFC 2253). For
 example, the system username is mapped to the `CN=system,O=Progress,C=US` subject DN. When
performing authentication,

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/1e81361a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java
----------------------------------------------------------------------
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java
b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java
index 2bced47..21b4d08 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java
@@ -150,16 +150,26 @@ public class SecurityTest extends ActiveMQTestBase {
 
    @Test
    public void testJAASSecurityManagerAuthenticationWithCerts() throws Exception {
-      testJAASSecurityManagerAuthenticationWithCerts(TransportConstants.NEED_CLIENT_AUTH_PROP_NAME);
+      testJAASSecurityManagerAuthenticationWithCerts("CertLogin", TransportConstants.NEED_CLIENT_AUTH_PROP_NAME);
    }
 
    @Test
    public void testJAASSecurityManagerAuthenticationWithCertsWantClientAuth() throws Exception
{
-      testJAASSecurityManagerAuthenticationWithCerts(TransportConstants.WANT_CLIENT_AUTH_PROP_NAME);
+      testJAASSecurityManagerAuthenticationWithCerts("CertLogin", TransportConstants.WANT_CLIENT_AUTH_PROP_NAME);
    }
 
-   protected void testJAASSecurityManagerAuthenticationWithCerts(String clientAuthPropName)
throws Exception {
-      ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("CertLogin");
+   @Test
+   public void testJAASSecurityManagerAuthenticationWithRegexps() throws Exception {
+      testJAASSecurityManagerAuthenticationWithCerts("CertLoginWithRegexp", TransportConstants.NEED_CLIENT_AUTH_PROP_NAME);
+   }
+
+   @Test
+   public void testJAASSecurityManagerAuthenticationWithRegexpsWantClientAuth() throws Exception
{
+      testJAASSecurityManagerAuthenticationWithCerts("CertLoginWithRegexp", TransportConstants.WANT_CLIENT_AUTH_PROP_NAME);
+   }
+
+   protected void testJAASSecurityManagerAuthenticationWithCerts(String secManager, String
clientAuthPropName) throws Exception {
+      ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(secManager);
       ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true),
ManagementFactory.getPlatformMBeanServer(), securityManager, false));
 
       Map<String, Object> params = new HashMap<>();

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/1e81361a/tests/integration-tests/src/test/resources/cert-regexps.properties
----------------------------------------------------------------------
diff --git a/tests/integration-tests/src/test/resources/cert-regexps.properties b/tests/integration-tests/src/test/resources/cert-regexps.properties
new file mode 100644
index 0000000..9677bd8
--- /dev/null
+++ b/tests/integration-tests/src/test/resources/cert-regexps.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+first=/CN=ActiveMQ Artemis Client, OU=Artemis, O=ActiveMQ(, [A-Z]+=AMQ)+/
+second=O=Internet Widgits Pty Ltd, C=AU, ST=Some-State, CN=lakalkalaoioislkxn

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/1e81361a/tests/integration-tests/src/test/resources/login.config
----------------------------------------------------------------------
diff --git a/tests/integration-tests/src/test/resources/login.config b/tests/integration-tests/src/test/resources/login.config
index a5e40c1..19d684e 100644
--- a/tests/integration-tests/src/test/resources/login.config
+++ b/tests/integration-tests/src/test/resources/login.config
@@ -124,6 +124,13 @@ CertLogin {
         org.apache.activemq.jaas.textfiledn.role="cert-roles.properties";
 };
 
+CertLoginWithRegexp {
+    org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule required
+        debug=true
+        org.apache.activemq.jaas.textfiledn.user="cert-regexps.properties"
+        org.apache.activemq.jaas.textfiledn.role="cert-roles.properties";
+};
+
 DualAuthenticationCertLogin {
     org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule required
         debug=true


Mime
View raw message