From e8fc4975f5a758ee7204f89d1649cc326bcd5085 Mon Sep 17 00:00:00 2001 From: Lionel Cons Date: Wed, 11 Apr 2018 08:59:24 +0200 Subject: [PATCH 1/2] ARTEMIS-1740: Add support for regex based certificate authentication --- .../security/jaas/ReloadableProperties.java | 19 ++++++++++++ .../jaas/TextFileCertificateLoginModule.java | 31 +++++++++++++++++-- .../TextFileCertificateLoginModuleTest.java | 17 +++++++--- ...roles.properties => cert-empty.properties} | 0 .../test/resources/cert-regexps.properties | 18 +++++++++++ 5 files changed, 79 insertions(+), 6 deletions(-) rename artemis-server/src/test/resources/{cert-roles.properties => cert-empty.properties} (100%) create mode 100644 artemis-server/src/test/resources/cert-regexps.properties 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 bed7d5f278e..55ff2ccb6fb 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.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 invertedProps; private Map> invertedValueProps; + private Map regexpProps; private long reloadTime = -1; private final PropertiesLoader.FileNameKey key; @@ -53,6 +56,7 @@ public synchronized ReloadableProperties obtained() { load(key.file(), props); invertedProps = null; invertedValueProps = null; + regexpProps = null; if (key.isDebug()) { logger.debug("Load of: " + key); } @@ -95,6 +99,21 @@ public synchronized Map> invertedPropertiesValuesMap() { return invertedValueProps; } + public synchronized Map regexpPropertiesMap() { + if (regexpProps == null) { + regexpProps = new HashMap<>(props.size()); + for (Map.Entry val : props.entrySet()) { + try { + Pattern p = Pattern.compile((String) val.getValue()); + regexpProps.put((String) val.getKey(), p); + } catch (PatternSyntaxException e) { + ActiveMQServerLogger.LOGGER.warn("Ignoring invalid regexp: " + val.getValue()); + } + } + } + return regexpProps; + } + private void load(final File source, Properties props) throws IOException { try (FileInputStream in = new FileInputStream(source)) { props.load(in); 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 ca103cd156f..18dddeccb8c 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 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 @@ -39,9 +40,11 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule { private static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.user"; private static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.role"; + private static final String REGEXP_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.regexp"; private Map> rolesByUser; private Map usersByDn; + private Map regexpByUser; /** * Performs initialization of file paths. A standard JAAS override. @@ -54,6 +57,11 @@ public void initialize(Subject subject, super.initialize(subject, callbackHandler, sharedState, options); usersByDn = load(USER_FILE_PROP_NAME, "", options).invertedPropertiesMap(); rolesByUser = load(ROLE_FILE_PROP_NAME, "", options).invertedPropertiesValuesMap(); + if (options.get(REGEXP_FILE_PROP_NAME) != null) { + regexpByUser = load(REGEXP_FILE_PROP_NAME, "", options).regexpPropertiesMap(); + } else { + regexpByUser = null; + } } /** @@ -71,8 +79,12 @@ protected String getUserNameForCertificates(final X509Certificate[] certs) throw if (certs == null) { throw new LoginException("Client certificates not found. Cannot authenticate."); } - - return usersByDn.get(getDistinguishedName(certs)); + String dn = getDistinguishedName(certs); + String name = usersByDn.get(dn); + if (name == null && regexpByUser != null) { + name = getUserByRegexp(dn); + } + return name; } /** @@ -92,4 +104,19 @@ protected Set getUserRoles(String username) throws LoginException { return userRoles; } + + private synchronized String getUserByRegexp(String dn) { + String name = null; + for (Map.Entry val : regexpByUser.entrySet()) { + if (val.getValue().matcher(dn).matches()) { + name = val.getKey(); + break; + } + } + if (name != null) { + usersByDn.put(dn, name); + } + return name; + } + } 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 2eebd069894..ea15797a7b5 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 @@ -44,9 +44,10 @@ public class TextFileCertificateLoginModuleTest { private static final Logger logger = Logger.getLogger(TextFileCertificateLoginModuleTest.class); + private static final String CERT_EMPTY_FILE = "cert-empty.properties"; 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_GROUPS_FILE = "cert-roles.properties"; + private static final String CERT_REGEXPS_FILE = "cert-regexps.properties"; private static final int NUMBER_SUBJECTS = 10; @@ -80,19 +81,27 @@ public void tearDown() throws Exception { @Test public void testLoginWithSMALLUsersFile() throws Exception { - loginTest(CERT_USERS_FILE_SMALL, CERT_GROUPS_FILE); + loginTest(CERT_USERS_FILE_SMALL, CERT_EMPTY_FILE, null); } @Test public void testLoginWithLARGEUsersFile() throws Exception { - loginTest(CERT_USERS_FILE_LARGE, CERT_GROUPS_FILE); + loginTest(CERT_USERS_FILE_LARGE, CERT_EMPTY_FILE, null); } - private void loginTest(String usersFiles, String groupsFile) throws LoginException { + @Test + public void testLoginWithRegexpsFile() throws Exception { + loginTest(CERT_EMPTY_FILE, CERT_EMPTY_FILE, CERT_REGEXPS_FILE); + } + + private void loginTest(String usersFiles, String groupsFile, String regexpsFile) throws LoginException { HashMap options = new HashMap<>(); options.put("org.apache.activemq.jaas.textfiledn.user", usersFiles); options.put("org.apache.activemq.jaas.textfiledn.role", groupsFile); + if (regexpsFile != null) { + options.put("org.apache.activemq.jaas.textfiledn.regexp", regexpsFile); + } options.put("reload", "true"); JaasCallbackHandler[] callbackHandlers = new JaasCallbackHandler[NUMBER_SUBJECTS]; diff --git a/artemis-server/src/test/resources/cert-roles.properties b/artemis-server/src/test/resources/cert-empty.properties similarity index 100% rename from artemis-server/src/test/resources/cert-roles.properties rename to artemis-server/src/test/resources/cert-empty.properties diff --git a/artemis-server/src/test/resources/cert-regexps.properties b/artemis-server/src/test/resources/cert-regexps.properties new file mode 100644 index 00000000000..a909f4fe757 --- /dev/null +++ b/artemis-server/src/test/resources/cert-regexps.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] From 9ead970395975a28846de4ba237eb78bc728c8fb Mon Sep 17 00:00:00 2001 From: Lionel Cons Date: Thu, 12 Apr 2018 10:25:21 +0200 Subject: [PATCH 2/2] improved code, tests and documentation - not using a separate file for regexps anymore - added negative caching - added more tests - added documentation --- .../security/jaas/ReloadableProperties.java | 23 ++++++++++++++----- .../jaas/TextFileCertificateLoginModule.java | 19 ++++----------- .../TextFileCertificateLoginModuleTest.java | 17 ++++++-------- ...empty.properties => cert-roles.properties} | 0 ...roperties => cert-users-REGEXP.properties} | 4 ++-- docs/user-manual/en/security.md | 7 ++++-- .../integration/security/SecurityTest.java | 18 +++++++++++---- .../test/resources/cert-regexps.properties | 19 +++++++++++++++ .../src/test/resources/login.config | 7 ++++++ 9 files changed, 75 insertions(+), 39 deletions(-) rename artemis-server/src/test/resources/{cert-empty.properties => cert-roles.properties} (100%) rename artemis-server/src/test/resources/{cert-regexps.properties => cert-users-REGEXP.properties} (93%) create mode 100644 tests/integration-tests/src/test/resources/cert-regexps.properties 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 55ff2ccb6fb..947c6d164bd 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 @@ -75,7 +75,10 @@ public synchronized Map invertedPropertiesMap() { if (invertedProps == null) { invertedProps = new HashMap<>(props.size()); for (Map.Entry 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; @@ -103,11 +106,14 @@ public synchronized Map regexpPropertiesMap() { if (regexpProps == null) { regexpProps = new HashMap<>(props.size()); for (Map.Entry val : props.entrySet()) { - try { - Pattern p = Pattern.compile((String) val.getValue()); - regexpProps.put((String) val.getKey(), p); - } catch (PatternSyntaxException e) { - ActiveMQServerLogger.LOGGER.warn("Ignoring invalid regexp: " + val.getValue()); + 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); + } } } } @@ -134,4 +140,9 @@ private boolean hasModificationAfter(long reloadTime) { 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/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 18dddeccb8c..2b0f45cd452 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 @@ -40,11 +40,10 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule { private static final String USER_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.user"; private static final String ROLE_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.role"; - private static final String REGEXP_FILE_PROP_NAME = "org.apache.activemq.jaas.textfiledn.regexp"; private Map> rolesByUser; - private Map usersByDn; private Map regexpByUser; + private Map usersByDn; /** * Performs initialization of file paths. A standard JAAS override. @@ -56,12 +55,8 @@ public void initialize(Subject subject, Map 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(); - if (options.get(REGEXP_FILE_PROP_NAME) != null) { - regexpByUser = load(REGEXP_FILE_PROP_NAME, "", options).regexpPropertiesMap(); - } else { - regexpByUser = null; - } } /** @@ -80,11 +75,7 @@ protected String getUserNameForCertificates(final X509Certificate[] certs) throw throw new LoginException("Client certificates not found. Cannot authenticate."); } String dn = getDistinguishedName(certs); - String name = usersByDn.get(dn); - if (name == null && regexpByUser != null) { - name = getUserByRegexp(dn); - } - return name; + return usersByDn.containsKey(dn) ? usersByDn.get(dn) : getUserByRegexp(dn); } /** @@ -113,9 +104,7 @@ private synchronized String getUserByRegexp(String dn) { break; } } - if (name != null) { - usersByDn.put(dn, name); - } + usersByDn.put(dn, name); return name; } 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 ea15797a7b5..957dace03e2 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 @@ -44,10 +44,10 @@ public class TextFileCertificateLoginModuleTest { private static final Logger logger = Logger.getLogger(TextFileCertificateLoginModuleTest.class); - private static final String CERT_EMPTY_FILE = "cert-empty.properties"; 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_REGEXPS_FILE = "cert-regexps.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; @@ -81,27 +81,24 @@ public void tearDown() throws Exception { @Test public void testLoginWithSMALLUsersFile() throws Exception { - loginTest(CERT_USERS_FILE_SMALL, CERT_EMPTY_FILE, null); + loginTest(CERT_USERS_FILE_SMALL, CERT_GROUPS_FILE); } @Test public void testLoginWithLARGEUsersFile() throws Exception { - loginTest(CERT_USERS_FILE_LARGE, CERT_EMPTY_FILE, null); + loginTest(CERT_USERS_FILE_LARGE, CERT_GROUPS_FILE); } @Test - public void testLoginWithRegexpsFile() throws Exception { - loginTest(CERT_EMPTY_FILE, CERT_EMPTY_FILE, CERT_REGEXPS_FILE); + public void testLoginWithREGEXPUsersFile() throws Exception { + loginTest(CERT_USERS_FILE_REGEXP, CERT_GROUPS_FILE); } - private void loginTest(String usersFiles, String groupsFile, String regexpsFile) throws LoginException { + private void loginTest(String usersFiles, String groupsFile) throws LoginException { HashMap options = new HashMap<>(); options.put("org.apache.activemq.jaas.textfiledn.user", usersFiles); options.put("org.apache.activemq.jaas.textfiledn.role", groupsFile); - if (regexpsFile != null) { - options.put("org.apache.activemq.jaas.textfiledn.regexp", regexpsFile); - } options.put("reload", "true"); JaasCallbackHandler[] callbackHandlers = new JaasCallbackHandler[NUMBER_SUBJECTS]; diff --git a/artemis-server/src/test/resources/cert-empty.properties b/artemis-server/src/test/resources/cert-roles.properties similarity index 100% rename from artemis-server/src/test/resources/cert-empty.properties rename to artemis-server/src/test/resources/cert-roles.properties diff --git a/artemis-server/src/test/resources/cert-regexps.properties b/artemis-server/src/test/resources/cert-users-REGEXP.properties similarity index 93% rename from artemis-server/src/test/resources/cert-regexps.properties rename to artemis-server/src/test/resources/cert-users-REGEXP.properties index a909f4fe757..22e67a69af4 100644 --- a/artemis-server/src/test/resources/cert-regexps.properties +++ b/artemis-server/src/test/resources/cert-users-REGEXP.properties @@ -14,5 +14,5 @@ ## 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] +CNODD=/DN=TEST_USER_\\d*[13579]/ +CNEVEN=/DN=TEST_USER_\\d*[02468]/ diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md index ac0c76f9982..2f707014936 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, 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 2bced4783fb..21b4d082985 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 void testJAASSecurityManagerAuthenticationWithValidateUser() throws Excep @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 params = new HashMap<>(); 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 00000000000..9677bd81ae5 --- /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 diff --git a/tests/integration-tests/src/test/resources/login.config b/tests/integration-tests/src/test/resources/login.config index a5e40c159a4..19d684e39b6 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