mirror of https://github.com/apache/activemq.git
Merge pull request #367 from LionelCons/amq_7230
AMQ-7230 - Add support for regex based certificate authentication
This commit is contained in:
commit
6ec56912fe
|
@ -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) == '/';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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]/
|
Loading…
Reference in New Issue