You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by cl...@apache.org on 2015/11/09 21:41:45 UTC
[3/4] activemq-artemis git commit: ARTEMIS-299 file-based login
module performance
ARTEMIS-299 file-based login module performance
This is based on improvements made in ActiveMQ 5.x. See more at
https://issues.apache.org/jira/browse/AMQ-5876.
Project: http://git-wip-us.apache.org/repos/asf/activemq-artemis/repo
Commit: http://git-wip-us.apache.org/repos/asf/activemq-artemis/commit/ae70d717
Tree: http://git-wip-us.apache.org/repos/asf/activemq-artemis/tree/ae70d717
Diff: http://git-wip-us.apache.org/repos/asf/activemq-artemis/diff/ae70d717
Branch: refs/heads/master
Commit: ae70d7178e201dff5b13ac9c6cb63146d29fc4b9
Parents: 08e5245
Author: jbertram <jb...@apache.org>
Authored: Wed Nov 4 16:38:40 2015 -0600
Committer: jbertram <jb...@apache.org>
Committed: Thu Nov 5 10:10:49 2015 -0600
----------------------------------------------------------------------
.../security/jaas/CertificateLoginModule.java | 9 +-
.../core/security/jaas/PrincipalProperties.java | 75 -
.../core/security/jaas/PropertiesLoader.java | 137 +
.../security/jaas/PropertiesLoginModule.java | 86 +-
.../security/jaas/ReloadableProperties.java | 99 +
.../jaas/TextFileCertificateLoginModule.java | 62 +-
.../PropertiesLoginModuleRaceConditionTest.java | 3 +-
.../core/security/jaas/StubX509Certificate.java | 137 +
.../TextFileCertificateLoginModuleTest.java | 123 +
.../src/test/resources/cert-roles.properties | 16 +
.../test/resources/cert-users-LARGE.properties | 200026 ++++++++++++++++
.../test/resources/cert-users-SMALL.properties | 116 +
12 files changed, 200682 insertions(+), 207 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java
index fbacb31..20d107e 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java
@@ -37,7 +37,7 @@ import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
* Allows for subclasses to define methods used to verify user certificates and
* find user roles. Uses CertificateCallbacks to retrieve certificates.
*/
-public abstract class CertificateLoginModule implements LoginModule {
+public abstract class CertificateLoginModule extends PropertiesLoader implements LoginModule {
private CallbackHandler callbackHandler;
private Subject subject;
@@ -46,7 +46,6 @@ public abstract class CertificateLoginModule implements LoginModule {
private String username;
private Set<String> roles;
private Set<Principal> principals = new HashSet<Principal>();
- private boolean debug;
/**
* Overriding to allow for proper initialization. Standard JAAS.
@@ -56,11 +55,7 @@ public abstract class CertificateLoginModule implements LoginModule {
this.subject = subject;
this.callbackHandler = callbackHandler;
- debug = "true".equalsIgnoreCase((String) options.get("debug"));
-
- if (debug) {
- ActiveMQServerLogger.LOGGER.debug("Initialized debug");
- }
+ init(options);
}
/**
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PrincipalProperties.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PrincipalProperties.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PrincipalProperties.java
deleted file mode 100644
index 97e2355..0000000
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PrincipalProperties.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.spi.core.security.jaas;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-
-import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
-
-class PrincipalProperties {
-
- private final Properties principals;
- private final long reloadTime;
-
- PrincipalProperties(final String type, final File source, final ActiveMQServerLogger log) {
- Properties props = new Properties();
- long reloadTime = 0;
- try {
- load(source, props);
- reloadTime = System.currentTimeMillis();
- }
- catch (IOException ioe) {
- ioe.printStackTrace();
- log.warn("Unable to load " + type + " properties file " + source);
- }
- this.reloadTime = reloadTime;
- this.principals = props;
- }
-
- @SuppressWarnings({"unchecked", "rawtypes"})
- Set<Map.Entry<String, String>> entries() {
- return (Set) principals.entrySet();
- }
-
- String getProperty(String name) {
- return principals.getProperty(name);
- }
-
- long getReloadTime() {
- return reloadTime;
- }
-
- private void load(final File source, Properties props) throws FileNotFoundException, IOException {
- FileInputStream in = new FileInputStream(source);
- try {
- props.load(in);
- }
- finally {
- in.close();
- }
- }
-
- Properties getPrincipals() {
- return principals;
- }
-}
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoader.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoader.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoader.java
new file mode 100644
index 0000000..89b80db
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoader.java
@@ -0,0 +1,137 @@
+/*
+ * 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.spi.core.security.jaas;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+
+public class PropertiesLoader {
+
+ static Map<FileNameKey, ReloadableProperties> staticCache = new HashMap<FileNameKey, ReloadableProperties>();
+ protected boolean debug;
+
+ public void init(Map options) {
+ debug = booleanOption("debug", options);
+ if (debug) {
+ ActiveMQServerLogger.LOGGER.debug("Initialized debug");
+ }
+ }
+
+ public ReloadableProperties load(String nameProperty, String fallbackName, Map options) {
+ ReloadableProperties result;
+ FileNameKey key = new FileNameKey(nameProperty, fallbackName, options);
+ key.setDebug(debug);
+
+ synchronized (staticCache) {
+ result = staticCache.get(key);
+ if (result == null) {
+ result = new ReloadableProperties(key);
+ staticCache.put(key, result);
+ }
+ }
+
+ return result.obtained();
+ }
+
+ private static boolean booleanOption(String name, Map options) {
+ return Boolean.parseBoolean((String) options.get(name));
+ }
+
+ public class FileNameKey {
+
+ final File file;
+ final String absPath;
+ final boolean reload;
+ private boolean decrypt;
+ private boolean debug;
+
+ public FileNameKey(String nameProperty, String fallbackName, Map options) {
+ this.file = new File(baseDir(options), stringOption(nameProperty, fallbackName, options));
+ absPath = file.getAbsolutePath();
+ reload = booleanOption("reload", options);
+ decrypt = booleanOption("decrypt", options);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof FileNameKey && this.absPath.equals(((FileNameKey) other).absPath);
+ }
+
+ public int hashCode() {
+ return this.absPath.hashCode();
+ }
+
+ public boolean isReload() {
+ return reload;
+ }
+
+ public File file() {
+ return file;
+ }
+
+ public boolean isDecrypt() {
+ return decrypt;
+ }
+
+ public void setDecrypt(boolean decrypt) {
+ this.decrypt = decrypt;
+ }
+
+ private String stringOption(String key, String nameDefault, Map options) {
+ Object result = options.get(key);
+ return result != null ? result.toString() : nameDefault;
+ }
+
+ private File baseDir(Map options) {
+ File baseDir = null;
+ if (options.get("baseDir") != null) {
+ baseDir = new File((String) options.get("baseDir"));
+ }
+ else {
+ if (System.getProperty("java.security.auth.login.config") != null) {
+ baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile();
+ }
+ }
+ if (debug) {
+ ActiveMQServerLogger.LOGGER.debug("Using basedir=" + baseDir.getAbsolutePath());
+ }
+ return baseDir;
+ }
+
+ public String toString() {
+ return "PropsFile=" + absPath;
+ }
+
+ public void setDebug(boolean debug) {
+ this.debug = debug;
+ }
+
+ public boolean isDebug() {
+ return debug;
+ }
+ }
+
+ /**
+ * For test-usage only.
+ */
+ public static void resetUsersAndGroupsCache() {
+ staticCache.clear();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/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 6b96ed0..0a75a24 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
@@ -25,87 +25,41 @@ import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
-import java.io.File;
import java.io.IOException;
import java.security.Principal;
import java.util.HashSet;
import java.util.Map;
+import java.util.Properties;
import java.util.Set;
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
-public class PropertiesLoginModule implements LoginModule {
+public class PropertiesLoginModule extends PropertiesLoader implements LoginModule {
- private static final String USER_FILE = "org.apache.activemq.jaas.properties.user";
- private static final String GROUP_FILE = "org.apache.activemq.jaas.properties.role";
+ 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";
private Subject subject;
private CallbackHandler callbackHandler;
- private boolean debug;
- private boolean reload = false;
- private static volatile PrincipalProperties users;
- private static volatile PrincipalProperties roles;
+ private Properties users;
+ private Properties roles;
private String user;
private final Set<Principal> principals = new HashSet<Principal>();
- private File baseDir;
private boolean loginSucceeded;
- // private boolean decrypt = true;
@Override
- public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
+ public void initialize(Subject subject,
+ CallbackHandler callbackHandler,
+ Map sharedState,
+ Map options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
loginSucceeded = false;
- debug = "true".equalsIgnoreCase((String) options.get("debug"));
- if (options.get("reload") != null) {
- reload = "true".equalsIgnoreCase((String) options.get("reload"));
- }
-
- if (options.get("baseDir") != null) {
- baseDir = new File((String) options.get("baseDir"));
- }
-
- setBaseDir();
- String usersFile = options.get(USER_FILE) + "";
- File uf = baseDir != null ? new File(baseDir, usersFile) : new File(usersFile);
-
- if (reload || users == null || uf.lastModified() > users.getReloadTime()) {
- if (debug) {
- ActiveMQServerLogger.LOGGER.debug("Reloading users from " + uf.getAbsolutePath());
- }
- users = new PrincipalProperties("user", uf, ActiveMQServerLogger.LOGGER);
- // if( decrypt ) {
- // try {
- // EncryptionSupport.decrypt(users.getPrincipals());
- // } catch(NoClassDefFoundError e) {
- // // this Happens whe jasypt is not on the classpath..
- // decrypt = false;
- // ActiveMQServerLogger.LOGGER.info("jasypt is not on the classpath: password decryption disabled.");
- // }
- // }
- }
-
- String groupsFile = options.get(GROUP_FILE) + "";
- File gf = baseDir != null ? new File(baseDir, groupsFile) : new File(groupsFile);
- if (reload || roles == null || gf.lastModified() > roles.getReloadTime()) {
- if (debug) {
- ActiveMQServerLogger.LOGGER.debug("Reloading roles from " + gf.getAbsolutePath());
- }
- roles = new PrincipalProperties("role", gf, ActiveMQServerLogger.LOGGER);
- }
- }
-
- private void setBaseDir() {
- if (baseDir == null) {
- if (System.getProperty("java.security.auth.login.config") != null) {
- baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile();
- if (debug) {
- ActiveMQServerLogger.LOGGER.debug("Using basedir=" + baseDir.getAbsolutePath());
- }
- }
- }
+ init(options);
+ users = load(USER_FILE_PROP_NAME, "user", options).getProps();
+ roles = load(ROLE_FILE_PROP_NAME, "role", options).getProps();
}
@Override
@@ -153,12 +107,12 @@ public class PropertiesLoginModule implements LoginModule {
if (result) {
principals.add(new UserPrincipal(user));
- for (Map.Entry<String, String> entry : roles.entries()) {
- String name = entry.getKey();
+ for (Map.Entry<Object, Object> entry : roles.entrySet()) {
+ String name = (String) entry.getKey();
+ String[] userList = ((String) entry.getValue()).split(",");
if (debug) {
ActiveMQServerLogger.LOGGER.debug("Inspecting role '" + name + "' with user(s): " + entry.getValue());
}
- String[] userList = entry.getValue().split(",");
for (int i = 0; i < userList.length; i++) {
if (user.equals(userList[i])) {
principals.add(new RolePrincipal(name));
@@ -204,12 +158,4 @@ public class PropertiesLoginModule implements LoginModule {
user = null;
loginSucceeded = false;
}
-
- /**
- * For test-usage only.
- */
- public static void resetUsersAndGroupsCache() {
- users = null;
- roles = null;
- }
}
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/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
new file mode 100644
index 0000000..46d10a5
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/ReloadableProperties.java
@@ -0,0 +1,99 @@
+/*
+ * 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.spi.core.security.jaas;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+
+public class ReloadableProperties {
+
+ private Properties props = new Properties();
+ private Map<String, String> invertedProps;
+ private long reloadTime = -1;
+ private final PropertiesLoader.FileNameKey key;
+
+ public ReloadableProperties(PropertiesLoader.FileNameKey key) {
+ this.key = key;
+ }
+
+ public synchronized Properties getProps() {
+ return props;
+ }
+
+ public synchronized ReloadableProperties obtained() {
+ if (reloadTime < 0 || (key.isReload() && hasModificationAfter(reloadTime))) {
+ props = new Properties();
+ try {
+ load(key.file(), props);
+ invertedProps = null;
+ if (key.isDebug()) {
+ ActiveMQServerLogger.LOGGER.debug("Load of: " + key);
+ }
+ }
+ catch (IOException e) {
+ ActiveMQServerLogger.LOGGER.error("Failed to load: " + key + ", reason:" + e.getLocalizedMessage());
+ if (key.isDebug()) {
+ ActiveMQServerLogger.LOGGER.debug("Load of: " + key + ", failure exception" + e);
+ }
+ }
+ reloadTime = System.currentTimeMillis();
+ }
+ return this;
+ }
+
+ public synchronized Map<String, String> invertedPropertiesMap() {
+ if (invertedProps == null) {
+ invertedProps = new HashMap<>(props.size());
+ for (Map.Entry<Object, Object> val : props.entrySet()) {
+ invertedProps.put((String) val.getValue(), (String) val.getKey());
+ }
+ }
+ return invertedProps;
+ }
+
+ private void load(final File source,
+ Properties props) throws IOException {
+ FileInputStream in = new FileInputStream(source);
+ try {
+ props.load(in);
+ // if (key.isDecrypt()) {
+ // try {
+ // EncryptionSupport.decrypt(this.props);
+ // } catch (NoClassDefFoundError e) {
+ // // this Happens whe jasypt is not on the classpath..
+ // key.setDecrypt(false);
+ // ActiveMQServerLogger.LOGGER.info("jasypt is not on the classpath: password decryption disabled.");
+ // }
+ // }
+
+ }
+ finally {
+ in.close();
+ }
+ }
+
+ private boolean hasModificationAfter(long reloadTime) {
+ return key.file.lastModified() > reloadTime;
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/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 68639db..b1e4ba7 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
@@ -20,8 +20,6 @@ import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.cert.X509Certificate;
-import java.io.File;
-import java.io.IOException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
@@ -37,16 +35,15 @@ import java.util.Set;
* org.apache.activemq.jaas.textfiledn.user and
* org.apache.activemq.jaas.textfiledn.role 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).
+ * live updates of allowed roles and users).
*/
public class TextFileCertificateLoginModule extends CertificateLoginModule {
- private static final String USER_FILE = "org.apache.activemq.jaas.textfiledn.user";
- private static final String ROLE_FILE = "org.apache.activemq.jaas.textfiledn.role";
+ 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 File baseDir;
- private String usersFilePathname;
- private String rolesFilePathname;
+ private Properties roles;
+ private Map<String, String> usersByDn;
/**
* Performs initialization of file paths. A standard JAAS override.
@@ -54,15 +51,8 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
super.initialize(subject, callbackHandler, sharedState, options);
- if (System.getProperty("java.security.auth.login.config") != null) {
- baseDir = new File(System.getProperty("java.security.auth.login.config")).getParentFile();
- }
- else {
- baseDir = new File(".");
- }
-
- usersFilePathname = options.get(USER_FILE) + "";
- rolesFilePathname = options.get(ROLE_FILE) + "";
+ usersByDn = load(USER_FILE_PROP_NAME, "", options).invertedPropertiesMap();
+ roles = load(ROLE_FILE_PROP_NAME, "", options).getProps();
}
/**
@@ -81,32 +71,7 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
throw new LoginException("Client certificates not found. Cannot authenticate.");
}
- File usersFile = new File(baseDir, usersFilePathname);
-
- Properties users = new Properties();
-
- try {
- java.io.FileInputStream in = new java.io.FileInputStream(usersFile);
- users.load(in);
- in.close();
- }
- catch (IOException ioe) {
- throw new LoginException("Unable to load user properties file " + usersFile);
- }
-
- String dn = getDistinguishedName(certs);
-
- Enumeration<Object> keys = users.keys();
- for (Enumeration<Object> vals = users.elements(); vals.hasMoreElements(); ) {
- if (vals.nextElement().equals(dn)) {
- return (String) keys.nextElement();
- }
- else {
- keys.nextElement();
- }
- }
-
- return null;
+ return usersByDn.get(getDistinguishedName(certs));
}
/**
@@ -119,17 +84,6 @@ public class TextFileCertificateLoginModule extends CertificateLoginModule {
*/
@Override
protected Set<String> getUserRoles(String username) throws LoginException {
- File rolesFile = new File(baseDir, rolesFilePathname);
-
- Properties roles = new Properties();
- try {
- java.io.FileInputStream in = new java.io.FileInputStream(rolesFile);
- roles.load(in);
- in.close();
- }
- catch (IOException ioe) {
- throw new LoginException("Unable to load role properties file " + rolesFile);
- }
Set<String> userRoles = new HashSet<String>();
for (Enumeration<Object> enumeration = roles.keys(); enumeration.hasMoreElements(); ) {
String groupName = (String) enumeration.nextElement();
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleRaceConditionTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleRaceConditionTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleRaceConditionTest.java
index eb71c4c..13907f8 100644
--- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleRaceConditionTest.java
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleRaceConditionTest.java
@@ -33,6 +33,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler;
+import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoader;
import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule;
import org.junit.After;
import org.junit.Before;
@@ -126,7 +127,7 @@ public class PropertiesLoginModuleRaceConditionTest {
public void after() throws InterruptedException {
pool.shutdown();
assertTrue(pool.awaitTermination(500, TimeUnit.SECONDS));
- PropertiesLoginModule.resetUsersAndGroupsCache();
+ PropertiesLoader.resetUsersAndGroupsCache();
}
@Test
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/StubX509Certificate.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/StubX509Certificate.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/StubX509Certificate.java
new file mode 100644
index 0000000..72295f1
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/StubX509Certificate.java
@@ -0,0 +1,137 @@
+/*
+ * 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.core.security.jaas;
+
+import javax.security.cert.X509Certificate;
+import java.math.BigInteger;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.util.Date;
+import java.util.Set;
+
+public class StubX509Certificate extends X509Certificate {
+
+ private final Principal id;
+
+ public StubX509Certificate(Principal id) {
+ this.id = id;
+ }
+
+ public Principal getSubjectDN() {
+ return this.id;
+ }
+
+ // --- Stubbed Methods ---
+ public void checkValidity() {
+ }
+
+ public void checkValidity(Date arg0) {
+ }
+
+ public int getVersion() {
+ return 0;
+ }
+
+ public BigInteger getSerialNumber() {
+ return null;
+ }
+
+ public Principal getIssuerDN() {
+ return null;
+ }
+
+ public Date getNotBefore() {
+ return null;
+ }
+
+ public Date getNotAfter() {
+ return null;
+ }
+
+ public byte[] getTBSCertificate() {
+ return null;
+ }
+
+ public byte[] getSignature() {
+ return null;
+ }
+
+ public String getSigAlgName() {
+ return null;
+ }
+
+ public String getSigAlgOID() {
+ return null;
+ }
+
+ public byte[] getSigAlgParams() {
+ return null;
+ }
+
+ public boolean[] getIssuerUniqueID() {
+ return null;
+ }
+
+ public boolean[] getSubjectUniqueID() {
+ return null;
+ }
+
+ public boolean[] getKeyUsage() {
+ return null;
+ }
+
+ public int getBasicConstraints() {
+ return 0;
+ }
+
+ public byte[] getEncoded() {
+ return null;
+ }
+
+ public void verify(PublicKey arg0) {
+ }
+
+ public void verify(PublicKey arg0, String arg1) {
+ }
+
+ public String toString() {
+ return null;
+ }
+
+ public PublicKey getPublicKey() {
+ return null;
+ }
+
+ public boolean hasUnsupportedCriticalExtension() {
+ return false;
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public Set getCriticalExtensionOIDs() {
+ return null;
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public Set getNonCriticalExtensionOIDs() {
+ return null;
+ }
+
+ public byte[] getExtensionValue(String arg0) {
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/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
new file mode 100644
index 0000000..0eb736c
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/TextFileCertificateLoginModuleTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.core.security.jaas;
+
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginException;
+import javax.security.cert.X509Certificate;
+import java.net.URL;
+import java.util.HashMap;
+
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+import org.apache.activemq.artemis.spi.core.security.jaas.CertificateLoginModule;
+import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler;
+import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoader;
+import org.apache.activemq.artemis.spi.core.security.jaas.TextFileCertificateLoginModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+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_GROUPS_FILE = "cert-roles.properties";
+
+ private static final int NUMBER_SUBJECTS = 10;
+
+ static {
+ String path = System.getProperty("java.security.auth.login.config");
+ if (path == null) {
+ URL resource = TextFileCertificateLoginModuleTest.class.getClassLoader().getResource("login.config");
+ if (resource != null) {
+ path = resource.getFile();
+ System.setProperty("java.security.auth.login.config", path);
+ }
+ }
+ }
+
+ private CertificateLoginModule loginModule;
+
+ @Before
+ public void setUp() throws Exception {
+ loginModule = new TextFileCertificateLoginModule();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ PropertiesLoader.resetUsersAndGroupsCache();
+ }
+
+ @Test
+ public void testLoginWithSMALLUsersFile() throws Exception {
+ loginTest(CERT_USERS_FILE_SMALL, CERT_GROUPS_FILE);
+ }
+
+ @Test
+ public void testLoginWithLARGEUsersFile() throws Exception {
+ loginTest(CERT_USERS_FILE_LARGE, CERT_GROUPS_FILE);
+ }
+
+ private void loginTest(String usersFiles, String groupsFile) throws LoginException {
+
+ HashMap options = new HashMap<String, String>();
+ options.put("org.apache.activemq.jaas.textfiledn.user", usersFiles);
+ options.put("org.apache.activemq.jaas.textfiledn.role", groupsFile);
+ options.put("reload", "true");
+
+ JaasCallbackHandler[] callbackHandlers = new JaasCallbackHandler[NUMBER_SUBJECTS];
+ Subject[] subjects = new Subject[NUMBER_SUBJECTS];
+
+ for (int i = 0; i < callbackHandlers.length; i++) {
+ callbackHandlers[i] = getJaasCertificateCallbackHandler("DN=TEST_USER_" + (i + 1));
+ }
+
+ long startTime = System.currentTimeMillis();
+
+ for (int outer = 0; outer < 500; outer++) {
+ for (int i = 0; i < NUMBER_SUBJECTS; i++) {
+ Subject subject = doAuthenticate(options, callbackHandlers[i]);
+ subjects[i] = subject;
+ }
+ }
+
+ long endTime = System.currentTimeMillis();
+ long timeTaken = endTime - startTime;
+
+ for (int i = 0; i < NUMBER_SUBJECTS; i++) {
+ ActiveMQServerLogger.LOGGER.info("subject is: " + subjects[i].getPrincipals().toString());
+ }
+
+ ActiveMQServerLogger.LOGGER.info(usersFiles + ": Time taken is " + timeTaken);
+
+ }
+
+ private JaasCallbackHandler getJaasCertificateCallbackHandler(String user) {
+ JMXPrincipal principal = new JMXPrincipal(user);
+ X509Certificate cert = new StubX509Certificate(principal);
+ return new JaasCallbackHandler(null, null, new X509Certificate[]{cert});
+ }
+
+ private Subject doAuthenticate(HashMap options, JaasCallbackHandler callbackHandler) throws LoginException {
+ Subject mySubject = new Subject();
+ loginModule.initialize(mySubject, callbackHandler, null, options);
+ loginModule.login();
+ loginModule.commit();
+ return mySubject;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/ae70d717/artemis-server/src/test/resources/cert-roles.properties
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/resources/cert-roles.properties b/artemis-server/src/test/resources/cert-roles.properties
new file mode 100644
index 0000000..aab571c
--- /dev/null
+++ b/artemis-server/src/test/resources/cert-roles.properties
@@ -0,0 +1,16 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------