You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by jb...@apache.org on 2019/08/07 05:43:53 UTC
[activemq] branch master updated: AMQ-7230 - Add support for regex
based certificate authentication
This is an automated email from the ASF dual-hosted git repository.
jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/activemq.git
The following commit(s) were added to refs/heads/master by this push:
new c492763 AMQ-7230 - Add support for regex based certificate authentication
new 6ec56912 Merge pull request #367 from LionelCons/amq_7230
c492763 is described below
commit c4927638da9797df2d740a3e6694092b20228a34
Author: Lionel Cons <li...@cern.ch>
AuthorDate: Mon Jun 17 13:39:13 2019 +0200
AMQ-7230 - Add support for regex based certificate authentication
---
.../apache/activemq/jaas/ReloadableProperties.java | 32 +++++++++++++++++++++-
.../jaas/TextFileCertificateLoginModule.java | 31 ++++++++++++++++-----
.../TextFileCertificateLoginModuleTest.java | 6 ++++
.../test/resources/cert-users-REGEXP.properties | 19 +++++++++++++
4 files changed, 80 insertions(+), 8 deletions(-)
diff --git a/activemq-jaas/src/main/java/org/apache/activemq/jaas/ReloadableProperties.java b/activemq-jaas/src/main/java/org/apache/activemq/jaas/ReloadableProperties.java
index 95781cc..42427d0 100644
--- a/activemq-jaas/src/main/java/org/apache/activemq/jaas/ReloadableProperties.java
+++ b/activemq-jaas/src/main/java/org/apache/activemq/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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,6 +35,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;
@@ -51,6 +54,7 @@ public class ReloadableProperties {
load(key.file(), props);
invertedProps = null;
invertedValueProps = null;
+ regexpProps = null;
if (key.isDebug()) {
LOG.debug("Load of: " + key);
}
@@ -69,7 +73,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;
@@ -93,6 +100,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) {
+ LOG.warn("Ignoring invalid regexp: " + str);
+ }
+ }
+ }
+ }
+ return regexpProps;
+ }
+
private void load(final File source, Properties props) throws IOException {
FileInputStream in = new FileInputStream(source);
try {
@@ -116,4 +141,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) == '/';
+ }
+
}
diff --git a/activemq-jaas/src/main/java/org/apache/activemq/jaas/TextFileCertificateLoginModule.java b/activemq-jaas/src/main/java/org/apache/activemq/jaas/TextFileCertificateLoginModule.java
index 42f2c9d..c316367 100644
--- a/activemq-jaas/src/main/java/org/apache/activemq/jaas/TextFileCertificateLoginModule.java
+++ b/activemq-jaas/src/main/java/org/apache/activemq/jaas/TextFileCertificateLoginModule.java
@@ -24,6 +24,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
+import java.util.regex.Pattern;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
@@ -32,13 +33,14 @@ import javax.security.auth.login.LoginException;
/**
* A LoginModule allowing for SSL certificate based authentication based on
* Distinguished Names (DN) stored in text files. The DNs are parsed using a
- * Properties class where each line is <user_name>=<user_DN>. This class also
- * uses a group definition file where each line is <group_name>=<user_name_1>,<user_name_2>,etc.
+ * Properties class where each line is either <UserName>=<StringifiedSubjectDN>
+ * or <UserName>=/<SubjectDNRegExp>/. This class also uses a group definition
+ * file where each line is <GroupName>=<UserName1>,<UserName2>,etc.
* The user and group files' locations must be specified in the
* org.apache.activemq.jaas.textfiledn.user and
- * org.apache.activemq.jaas.textfiledn.user properties respectively. NOTE: This
- * class will re-read user and group files for every authentication (i.e it does
- * live updates of allowed groups and users).
+ * org.apache.activemq.jaas.textfiledn.group properties respectively.
+ * NOTE: This class will re-read user and group files for every authentication
+ * (i.e it does live updates of allowed groups and users).
*
* @author sepandm@gmail.com (Sepand)
*/
@@ -48,6 +50,7 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
private static final String GROUP_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.group";
private Map<String, Set<String>> groupsByUser;
+ private Map<String, Pattern> regexpByUser;
private Map<String, String> usersByDn;
/**
@@ -58,6 +61,7 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
super.initialize(subject, callbackHandler, sharedState, options);
usersByDn = load(USER_FILE_PROP_NAME, "", options).invertedPropertiesMap();
+ regexpByUser = load(USER_FILE_PROP_NAME, "", options).regexpPropertiesMap();
groupsByUser = load(GROUP_FILE_PROP_NAME, "", options).invertedPropertiesValuesMap();
}
@@ -76,8 +80,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);
}
/**
@@ -96,4 +100,17 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
}
return userGroups;
}
+
+ 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;
+ }
+
}
diff --git a/activemq-unit-tests/src/test/java/org/apache/activemq/security/TextFileCertificateLoginModuleTest.java b/activemq-unit-tests/src/test/java/org/apache/activemq/security/TextFileCertificateLoginModuleTest.java
index 76681c6..9ca43c9 100644
--- a/activemq-unit-tests/src/test/java/org/apache/activemq/security/TextFileCertificateLoginModuleTest.java
+++ b/activemq-unit-tests/src/test/java/org/apache/activemq/security/TextFileCertificateLoginModuleTest.java
@@ -38,6 +38,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-groups.properties";
private static final Logger LOG = LoggerFactory.getLogger(TextFileCertificateLoginModuleTest.class);
@@ -76,6 +77,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 options = new HashMap<String, String>();
diff --git a/activemq-unit-tests/src/test/resources/cert-users-REGEXP.properties b/activemq-unit-tests/src/test/resources/cert-users-REGEXP.properties
new file mode 100644
index 0000000..c422738
--- /dev/null
+++ b/activemq-unit-tests/src/test/resources/cert-users-REGEXP.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.
+## ---------------------------------------------------------------------------
+
+CNODD=/DN=TEST_USER_\\d*[13579]/
+CNEVEN=/DN=TEST_USER_\\d*[02468]/