activemq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From clebertsuco...@apache.org
Subject [3/5] activemq-artemis git commit: ARTEMIS-74 import JAAS auth from 5.x
Date Fri, 09 Oct 2015 19:58:11 GMT
http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginProperty.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginProperty.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginProperty.java
new file mode 100644
index 0000000..139d6c6
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginProperty.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+public class LDAPLoginProperty {
+
+   private String name;
+   private String value;
+
+   public LDAPLoginProperty(String name) {
+      this.name = name;
+   }
+
+   public LDAPLoginProperty(String name, String value) {
+      this.name = name;
+      this.value = value;
+   }
+
+   public String getPropertyName() {
+      return this.name;
+   }
+
+   public String getPropertyValue() {
+      return this.value;
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/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
new file mode 100644
index 0000000..97e2355
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PrincipalProperties.java
@@ -0,0 +1,75 @@
+/*
+ * 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/6ed9c5ae/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
new file mode 100644
index 0000000..6b96ed0
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModule.java
@@ -0,0 +1,215 @@
+/*
+ * 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 javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+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.Set;
+
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+
+public class PropertiesLoginModule 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 Subject subject;
+   private CallbackHandler callbackHandler;
+
+   private boolean debug;
+   private boolean reload = false;
+   private static volatile PrincipalProperties users;
+   private static volatile PrincipalProperties 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) {
+      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());
+            }
+         }
+      }
+   }
+
+   @Override
+   public boolean login() throws LoginException {
+      Callback[] callbacks = new Callback[2];
+
+      callbacks[0] = new NameCallback("Username: ");
+      callbacks[1] = new PasswordCallback("Password: ", false);
+      try {
+         callbackHandler.handle(callbacks);
+      }
+      catch (IOException ioe) {
+         throw new LoginException(ioe.getMessage());
+      }
+      catch (UnsupportedCallbackException uce) {
+         throw new LoginException(uce.getMessage() + " not available to obtain information from user");
+      }
+      user = ((NameCallback) callbacks[0]).getName();
+      char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
+      if (tmpPassword == null) {
+         tmpPassword = new char[0];
+      }
+      if (user == null) {
+         throw new FailedLoginException("user name is null");
+      }
+      String password = users.getProperty(user);
+
+      if (password == null) {
+         throw new FailedLoginException("User does exist");
+      }
+      if (!password.equals(new String(tmpPassword))) {
+         throw new FailedLoginException("Password does not match");
+      }
+      loginSucceeded = true;
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("login " + user);
+      }
+      return loginSucceeded;
+   }
+
+   @Override
+   public boolean commit() throws LoginException {
+      boolean result = loginSucceeded;
+      if (result) {
+         principals.add(new UserPrincipal(user));
+
+         for (Map.Entry<String, String> entry : roles.entries()) {
+            String name = entry.getKey();
+            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));
+                  break;
+               }
+            }
+         }
+
+         subject.getPrincipals().addAll(principals);
+      }
+
+      // will whack loginSucceeded
+      clear();
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("commit, result: " + result);
+      }
+      return result;
+   }
+
+   @Override
+   public boolean abort() throws LoginException {
+      clear();
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("abort");
+      }
+      return true;
+   }
+
+   @Override
+   public boolean logout() throws LoginException {
+      subject.getPrincipals().removeAll(principals);
+      principals.clear();
+      clear();
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("logout");
+      }
+      return true;
+   }
+
+   private void clear() {
+      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/6ed9c5ae/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/RolePrincipal.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/RolePrincipal.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/RolePrincipal.java
new file mode 100644
index 0000000..3e5afd1
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/RolePrincipal.java
@@ -0,0 +1,68 @@
+/*
+ * 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.security.Principal;
+
+public class RolePrincipal implements Principal {
+
+   private final String name;
+   private transient int hash;
+
+   public RolePrincipal(String name) {
+      if (name == null) {
+         throw new IllegalArgumentException("name cannot be null");
+      }
+      this.name = name;
+   }
+
+   @Override
+   public String getName() {
+      return name;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) {
+         return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+         return false;
+      }
+
+      final RolePrincipal that = (RolePrincipal) o;
+
+      if (!name.equals(that.name)) {
+         return false;
+      }
+
+      return true;
+   }
+
+   @Override
+   public int hashCode() {
+      if (hash == 0) {
+         hash = name.hashCode();
+      }
+      return hash;
+   }
+
+   @Override
+   public String toString() {
+      return name;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/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
new file mode 100644
index 0000000..77f5a43
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/TextFileCertificateLoginModule.java
@@ -0,0 +1,147 @@
+/*
+ * 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 javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.LoginException;
+import java.io.File;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * 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.
+ * 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).
+ */
+public class TextFileCertificateLoginModule extends CertificateLoginModule {
+
+   private static final String USER_FILE = "org.apache.activemq.jaas.textfiledn.user";
+   private static final String GROUP_FILE = "org.apache.activemq.jaas.textfiledn.group";
+
+   private File baseDir;
+   private String usersFilePathname;
+   private String groupsFilePathname;
+
+   /**
+    * Performs initialization of file paths. A standard JAAS override.
+    */
+   @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 = (String) options.get(USER_FILE) + "";
+      groupsFilePathname = (String) options.get(GROUP_FILE) + "";
+   }
+
+   /**
+    * Overriding to allow DN authorization based on DNs specified in text
+    * files.
+    *
+    * @param certs The certificate the incoming connection provided.
+    * @return The user's authenticated name or null if unable to authenticate
+    * the user.
+    * @throws LoginException Thrown if unable to find user file or connection
+    *                        certificate.
+    */
+   @Override
+   protected String getUserNameForCertificates(final X509Certificate[] certs) throws LoginException {
+      if (certs == null) {
+         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 (((String) vals.nextElement()).equals(dn)) {
+            return (String) keys.nextElement();
+         }
+         else {
+            keys.nextElement();
+         }
+      }
+
+      return null;
+   }
+
+   /**
+    * Overriding to allow for group discovery based on text files.
+    *
+    * @param username The name of the user being examined. This is the same
+    *                 name returned by getUserNameForCertificates.
+    * @return A Set of name Strings for groups this user belongs to.
+    * @throws LoginException Thrown if unable to find group definition file.
+    */
+   @Override
+   protected Set<String> getUserGroups(String username) throws LoginException {
+      File groupsFile = new File(baseDir, groupsFilePathname);
+
+      Properties groups = new Properties();
+      try {
+         java.io.FileInputStream in = new java.io.FileInputStream(groupsFile);
+         groups.load(in);
+         in.close();
+      }
+      catch (IOException ioe) {
+         throw new LoginException("Unable to load group properties file " + groupsFile);
+      }
+      Set<String> userGroups = new HashSet<String>();
+      for (Enumeration<Object> enumeration = groups.keys(); enumeration.hasMoreElements(); ) {
+         String groupName = (String) enumeration.nextElement();
+         String[] userList = (groups.getProperty(groupName) + "").split(",");
+         for (int i = 0; i < userList.length; i++) {
+            if (username.equals(userList[i])) {
+               userGroups.add(groupName);
+               break;
+            }
+         }
+      }
+
+      return userGroups;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/UserPrincipal.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/UserPrincipal.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/UserPrincipal.java
new file mode 100644
index 0000000..9e20f06
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/UserPrincipal.java
@@ -0,0 +1,68 @@
+/*
+ * 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.security.Principal;
+
+public class UserPrincipal implements Principal {
+
+   private final String name;
+   private transient int hash;
+
+   public UserPrincipal(String name) {
+      if (name == null) {
+         throw new IllegalArgumentException("name cannot be null");
+      }
+      this.name = name;
+   }
+
+   @Override
+   public String getName() {
+      return name;
+   }
+
+   @Override
+   public boolean equals(Object o) {
+      if (this == o) {
+         return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+         return false;
+      }
+
+      final UserPrincipal that = (UserPrincipal) o;
+
+      if (!name.equals(that.name)) {
+         return false;
+      }
+
+      return true;
+   }
+
+   @Override
+   public int hashCode() {
+      if (hash == 0) {
+         hash = name.hashCode();
+      }
+      return hash;
+   }
+
+   @Override
+   public String toString() {
+      return name;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/CertificateLoginModuleTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/CertificateLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/CertificateLoginModuleTest.java
new file mode 100644
index 0000000..9b3c33d
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/CertificateLoginModuleTest.java
@@ -0,0 +1,154 @@
+/*
+ * 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.auth.Subject;
+import javax.security.auth.login.LoginException;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.activemq.artemis.spi.core.security.jaas.JaasCertificateCallbackHandler;
+import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CertificateLoginModuleTest extends Assert {
+
+   private static final String USER_NAME = "testUser";
+   private static final List<String> GROUP_NAMES = new Vector<String>();
+
+   private StubCertificateLoginModule loginModule;
+
+   private Subject subject;
+
+   public CertificateLoginModuleTest() {
+      GROUP_NAMES.add("testGroup1");
+      GROUP_NAMES.add("testGroup2");
+      GROUP_NAMES.add("testGroup3");
+      GROUP_NAMES.add("testGroup4");
+   }
+
+   @Before
+   public void setUp() throws Exception {
+      subject = new Subject();
+   }
+
+   private void loginWithCredentials(String userName, Set<String> groupNames) throws LoginException {
+      loginModule = new StubCertificateLoginModule(userName, new HashSet<String>(groupNames));
+      JaasCertificateCallbackHandler callbackHandler = new JaasCertificateCallbackHandler(null);
+
+      loginModule.initialize(subject, callbackHandler, null, new HashMap());
+
+      loginModule.login();
+      loginModule.commit();
+   }
+
+   private void checkPrincipalsMatch(Subject subject) {
+      boolean nameFound = false;
+      boolean[] groupsFound = new boolean[GROUP_NAMES.size()];
+      for (int i = 0; i < groupsFound.length; ++i) {
+         groupsFound[i] = false;
+      }
+
+      for (Iterator iter = subject.getPrincipals().iterator(); iter.hasNext(); ) {
+         Principal currentPrincipal = (Principal) iter.next();
+
+         if (currentPrincipal instanceof UserPrincipal) {
+            if (currentPrincipal.getName().equals(USER_NAME)) {
+               if (!nameFound) {
+                  nameFound = true;
+               }
+               else {
+                  fail("UserPrincipal found twice.");
+               }
+
+            }
+            else {
+               fail("Unknown UserPrincipal found.");
+            }
+
+         }
+         else if (currentPrincipal instanceof RolePrincipal) {
+            int principalIdx = GROUP_NAMES.indexOf(((RolePrincipal) currentPrincipal).getName());
+
+            if (principalIdx < 0) {
+               fail("Unknown GroupPrincipal found.");
+            }
+
+            if (!groupsFound[principalIdx]) {
+               groupsFound[principalIdx] = true;
+            }
+            else {
+               fail("GroupPrincipal found twice.");
+            }
+         }
+         else {
+            fail("Unknown Principal type found.");
+         }
+      }
+   }
+
+   @Test
+   public void testLoginSuccess() throws IOException {
+      try {
+         loginWithCredentials(USER_NAME, new HashSet<String>(GROUP_NAMES));
+      }
+      catch (Exception e) {
+         fail("Unable to login: " + e.getMessage());
+      }
+
+      checkPrincipalsMatch(subject);
+   }
+
+   @Test
+   public void testLoginFailure() throws IOException {
+      boolean loginFailed = false;
+
+      try {
+         loginWithCredentials(null, new HashSet<String>());
+      }
+      catch (LoginException e) {
+         loginFailed = true;
+      }
+
+      if (!loginFailed) {
+         fail("Logged in with unknown certificate.");
+      }
+   }
+
+   @Test
+   public void testLogOut() throws IOException {
+      try {
+         loginWithCredentials(USER_NAME, new HashSet<String>(GROUP_NAMES));
+      }
+      catch (Exception e) {
+         fail("Unable to login: " + e.getMessage());
+      }
+
+      loginModule.logout();
+
+      assertEquals("logout should have cleared Subject principals.", 0, subject.getPrincipals().size());
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/GuestLoginModuleTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/GuestLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/GuestLoginModuleTest.java
new file mode 100644
index 0000000..54e743a
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/GuestLoginModuleTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class GuestLoginModuleTest extends Assert {
+
+   static {
+      String path = System.getProperty("java.security.auth.login.config");
+      if (path == null) {
+         URL resource = GuestLoginModuleTest.class.getClassLoader().getResource("login.config");
+         if (resource != null) {
+            path = resource.getFile();
+            System.setProperty("java.security.auth.login.config", path);
+         }
+      }
+   }
+
+   @Test
+   public void testLogin() throws LoginException {
+      LoginContext context = new LoginContext("GuestLogin", new CallbackHandler() {
+         public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            assertEquals("Should have no Callbacks", 0, callbacks.length);
+         }
+      });
+      context.login();
+
+      Subject subject = context.getSubject();
+
+      assertEquals("Should have two principals", 2, subject.getPrincipals().size());
+      assertEquals("Should have one user principal", 1, subject.getPrincipals(UserPrincipal.class).size());
+      assertTrue("User principal is 'foo'", subject.getPrincipals(UserPrincipal.class).contains(new UserPrincipal("foo")));
+
+      assertEquals("Should have one group principal", 1, subject.getPrincipals(RolePrincipal.class).size());
+      assertTrue("Role principal is 'bar'", subject.getPrincipals(RolePrincipal.class).contains(new RolePrincipal("bar")));
+
+      context.logout();
+
+      assertEquals("Should have zero principals", 0, subject.getPrincipals().size());
+   }
+
+   @Test
+   public void testLoginWithDefaults() throws LoginException {
+      LoginContext context = new LoginContext("GuestLoginWithDefaults", new CallbackHandler() {
+         public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            assertEquals("Should have no Callbacks", 0, callbacks.length);
+         }
+      });
+      context.login();
+
+      Subject subject = context.getSubject();
+
+      assertEquals("Should have two principals", 2, subject.getPrincipals().size());
+      assertEquals("Should have one user principal", 1, subject.getPrincipals(UserPrincipal.class).size());
+      assertTrue("User principal is 'guest'", subject.getPrincipals(UserPrincipal.class).contains(new UserPrincipal("guest")));
+
+      assertEquals("Should have one group principal", 1, subject.getPrincipals(RolePrincipal.class).size());
+      assertTrue("Role principal is 'guests'", subject.getPrincipals(RolePrincipal.class).contains(new RolePrincipal("guests")));
+
+      context.logout();
+
+      assertEquals("Should have zero principals", 0, subject.getPrincipals().size());
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java
new file mode 100644
index 0000000..f9bcbef
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPLoginModuleTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.naming.Context;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Hashtable;
+
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.ApplyLdifFiles;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(FrameworkRunner.class)
+@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP", port = 1024)})
+@ApplyLdifFiles("test.ldif")
+public class LDAPLoginModuleTest extends AbstractLdapTestUnit {
+
+   private static final String PRINCIPAL = "uid=admin,ou=system";
+   private static final String CREDENTIALS = "secret";
+
+   private final String loginConfigSysPropName = "java.security.auth.login.config";
+   private String oldLoginConfig;
+
+   @Before
+   public void setLoginConfigSysProperty() {
+      oldLoginConfig = System.getProperty(loginConfigSysPropName, null);
+      System.setProperty(loginConfigSysPropName, "src/test/resources/login.config");
+   }
+
+   @After
+   public void resetLoginConfigSysProperty() {
+      if (oldLoginConfig != null) {
+         System.setProperty(loginConfigSysPropName, oldLoginConfig);
+      }
+   }
+
+   @SuppressWarnings("unchecked")
+   @Test
+   public void testRunning() throws Exception {
+
+      Hashtable env = new Hashtable();
+      env.put(Context.PROVIDER_URL, "ldap://localhost:1024");
+      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+      env.put(Context.SECURITY_AUTHENTICATION, "simple");
+      env.put(Context.SECURITY_PRINCIPAL, PRINCIPAL);
+      env.put(Context.SECURITY_CREDENTIALS, CREDENTIALS);
+      DirContext ctx = new InitialDirContext(env);
+
+      HashSet set = new HashSet();
+
+      NamingEnumeration list = ctx.list("ou=system");
+
+      while (list.hasMore()) {
+         NameClassPair ncp = (NameClassPair) list.next();
+         set.add(ncp.getName());
+      }
+
+      assertTrue(set.contains("uid=admin"));
+      assertTrue(set.contains("ou=users"));
+      assertTrue(set.contains("ou=groups"));
+      assertTrue(set.contains("ou=configuration"));
+      assertTrue(set.contains("prefNodeName=sysPrefRoot"));
+
+   }
+
+   @Test
+   public void testLogin() throws LoginException {
+      LoginContext context = new LoginContext("LDAPLogin", new CallbackHandler() {
+         public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+               if (callbacks[i] instanceof NameCallback) {
+                  ((NameCallback) callbacks[i]).setName("first");
+               }
+               else if (callbacks[i] instanceof PasswordCallback) {
+                  ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray());
+               }
+               else {
+                  throw new UnsupportedCallbackException(callbacks[i]);
+               }
+            }
+         }
+      });
+      context.login();
+      context.logout();
+   }
+
+   @Test
+   public void testUnauthenticated() throws LoginException {
+      LoginContext context = new LoginContext("UnAuthenticatedLDAPLogin", new CallbackHandler() {
+         public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+               if (callbacks[i] instanceof NameCallback) {
+                  ((NameCallback) callbacks[i]).setName("first");
+               }
+               else if (callbacks[i] instanceof PasswordCallback) {
+                  ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray());
+               }
+               else {
+                  throw new UnsupportedCallbackException(callbacks[i]);
+               }
+            }
+         }
+      });
+      try {
+         context.login();
+      }
+      catch (LoginException le) {
+         assertEquals(le.getCause().getMessage(), "Empty password is not allowed");
+         return;
+      }
+      fail("Should have failed authenticating");
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPModuleRoleExpansionTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPModuleRoleExpansionTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPModuleRoleExpansionTest.java
new file mode 100644
index 0000000..b63f89d
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/LDAPModuleRoleExpansionTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.naming.Context;
+import javax.naming.NameClassPair;
+import javax.naming.NamingEnumeration;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Hashtable;
+
+import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+import org.apache.directory.server.annotations.CreateLdapServer;
+import org.apache.directory.server.annotations.CreateTransport;
+import org.apache.directory.server.core.annotations.ApplyLdifFiles;
+import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
+import org.apache.directory.server.core.integ.FrameworkRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+@RunWith(FrameworkRunner.class)
+@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP", port = 1024)})
+@ApplyLdifFiles("test.ldif")
+public class LDAPModuleRoleExpansionTest extends AbstractLdapTestUnit {
+
+   private static final String PRINCIPAL = "uid=admin,ou=system";
+   private static final String CREDENTIALS = "secret";
+   private final String loginConfigSysPropName = "java.security.auth.login.config";
+   private String oldLoginConfig;
+
+   @Before
+   public void setLoginConfigSysProperty() {
+      oldLoginConfig = System.getProperty(loginConfigSysPropName, null);
+      System.setProperty(loginConfigSysPropName, "src/test/resources/login.config");
+   }
+
+   @After
+   public void resetLoginConfigSysProperty() {
+      if (oldLoginConfig != null) {
+         System.setProperty(loginConfigSysPropName, oldLoginConfig);
+      }
+   }
+
+   @SuppressWarnings("unchecked")
+   @Test
+   public void testRunning() throws Exception {
+
+      Hashtable env = new Hashtable();
+      env.put(Context.PROVIDER_URL, "ldap://localhost:1024");
+      env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+      env.put(Context.SECURITY_AUTHENTICATION, "simple");
+      env.put(Context.SECURITY_PRINCIPAL, PRINCIPAL);
+      env.put(Context.SECURITY_CREDENTIALS, CREDENTIALS);
+      DirContext ctx = new InitialDirContext(env);
+
+      HashSet set = new HashSet();
+
+      NamingEnumeration list = ctx.list("ou=system");
+
+      while (list.hasMore()) {
+         NameClassPair ncp = (NameClassPair) list.next();
+         set.add(ncp.getName());
+      }
+
+      assertTrue(set.contains("uid=admin"));
+      assertTrue(set.contains("ou=users"));
+      assertTrue(set.contains("ou=groups"));
+      assertTrue(set.contains("ou=configuration"));
+      assertTrue(set.contains("prefNodeName=sysPrefRoot"));
+
+   }
+
+   @Test
+   public void testRoleExpansion() throws LoginException {
+      LoginContext context = new LoginContext("ExpandedLDAPLogin", new CallbackHandler() {
+         public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+               if (callbacks[i] instanceof NameCallback) {
+                  ((NameCallback) callbacks[i]).setName("first");
+               }
+               else if (callbacks[i] instanceof PasswordCallback) {
+                  ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray());
+               }
+               else {
+                  throw new UnsupportedCallbackException(callbacks[i]);
+               }
+            }
+         }
+      });
+      context.login();
+      Subject subject = context.getSubject();
+      boolean isAdmin = false;
+      boolean isUser = false;
+      for (Principal principal : subject.getPrincipals()) {
+         if (principal instanceof RolePrincipal) {
+            RolePrincipal groupPrincipal = (RolePrincipal) principal;
+            if (groupPrincipal.getName().equalsIgnoreCase("admins"))
+               isAdmin = true;
+            if (groupPrincipal.getName().equalsIgnoreCase("users"))
+               isUser = true;
+         }
+      }
+      // Should be in users by virtue of being in admins
+      assertTrue(isAdmin && isUser);
+      context.logout();
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/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
new file mode 100644
index 0000000..de72563
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleRaceConditionTest.java
@@ -0,0 +1,195 @@
+/*
+ * 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.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.activemq.artemis.spi.core.security.jaas.JaasCredentialCallbackHandler;
+import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestName;
+
+import static org.junit.Assert.assertTrue;
+
+public class PropertiesLoginModuleRaceConditionTest {
+
+   private static final String GROUPS_FILE = "roles.properties";
+   private static final String USERS_FILE = "users.properties";
+   private static final String USERNAME = "first";
+   private static final String PASSWORD = "secret";
+
+   @Rule
+   public final ErrorCollector e = new ErrorCollector();
+
+   @Rule
+   public final TemporaryFolder temp = new TemporaryFolder();
+
+   @Rule
+   public final TestName name = new TestName();
+
+   private Map<String, String> options;
+   private BlockingQueue<Exception> errors;
+   private ExecutorService pool;
+   private CallbackHandler callback;
+
+   private static class LoginTester implements Runnable {
+
+      private final CountDownLatch finished;
+      private final BlockingQueue<Exception> errors;
+      private final Map<String, String> options;
+      private final CountDownLatch start;
+      private final CallbackHandler callback;
+
+      LoginTester(CountDownLatch start,
+                  CountDownLatch finished,
+                  BlockingQueue<Exception> errors,
+                  Map<String, String> options,
+                  CallbackHandler callbackHandler) {
+         this.finished = finished;
+         this.errors = errors;
+         this.options = options;
+         this.start = start;
+         this.callback = callbackHandler;
+      }
+
+      @Override
+      public void run() {
+         try {
+            start.await();
+
+            Subject subject = new Subject();
+            PropertiesLoginModule module = new PropertiesLoginModule();
+            module.initialize(subject, callback, new HashMap<Object, Object>(), options);
+            module.login();
+            module.commit();
+         }
+         catch (Exception e) {
+            errors.offer(e);
+         }
+         finally {
+            finished.countDown();
+         }
+      }
+   }
+
+   @Before
+   public void before() throws FileNotFoundException, IOException {
+      createUsers();
+      createGroups();
+
+      options = new HashMap<String, String>();
+      options.put("reload", "true"); // Used to simplify reproduction of the
+      // race condition
+      options.put("org.apache.activemq.jaas.properties.user", USERS_FILE);
+      options.put("org.apache.activemq.jaas.properties.role", GROUPS_FILE);
+      options.put("baseDir", temp.getRoot().getAbsolutePath());
+
+      errors = new ArrayBlockingQueue<Exception>(processorCount());
+      pool = Executors.newFixedThreadPool(processorCount());
+      callback = new JaasCredentialCallbackHandler(USERNAME, PASSWORD);
+   }
+
+   @After
+   public void after() throws InterruptedException {
+      pool.shutdown();
+      assertTrue(pool.awaitTermination(500, TimeUnit.SECONDS));
+      PropertiesLoginModule.resetUsersAndGroupsCache();
+   }
+
+   @Test
+   public void raceConditionInUsersAndGroupsLoading() throws InterruptedException, FileNotFoundException, IOException {
+
+      // Brute force approach to increase the likelihood of the race condition occurring
+      for (int i = 0; i < 25000; i++) {
+         final CountDownLatch start = new CountDownLatch(1);
+         final CountDownLatch finished = new CountDownLatch(processorCount());
+         prepareLoginThreads(start, finished);
+
+         // Releases every login thread simultaneously to increase our chances of
+         // encountering the race condition
+         start.countDown();
+
+         finished.await();
+         if (isRaceConditionDetected()) {
+            e.addError(new AssertionError("At least one race condition in PropertiesLoginModule " + "has been encountered. Please examine the " + "following stack traces for more details:"));
+            for (Exception exception : errors) {
+               e.addError(exception);
+            }
+            return;
+         }
+      }
+   }
+
+   private boolean isRaceConditionDetected() {
+      return errors.size() > 0;
+   }
+
+   private void prepareLoginThreads(final CountDownLatch start, final CountDownLatch finished) {
+      for (int processor = 1; processor <= processorCount() * 2; processor++) {
+         pool.submit(new LoginTester(start, finished, errors, options, callback));
+      }
+   }
+
+   private int processorCount() {
+      return Runtime.getRuntime().availableProcessors();
+   }
+
+   private void store(Properties from, File to) throws FileNotFoundException, IOException {
+      FileOutputStream output = new FileOutputStream(to);
+      try {
+         from.store(output, "Generated by " + name.getMethodName());
+      }
+      finally {
+         output.close();
+      }
+   }
+
+   private void createGroups() throws FileNotFoundException, IOException {
+      Properties groups = new Properties();
+      for (int i = 0; i < 100; i++) {
+         groups.put("group" + i, "first,second,third");
+      }
+      store(groups, temp.newFile(GROUPS_FILE));
+   }
+
+   private void createUsers() throws FileNotFoundException, IOException {
+      Properties users = new Properties();
+      users.put(USERNAME, PASSWORD);
+      users.put("second", PASSWORD);
+      users.put("third", PASSWORD);
+      store(users, temp.newFile(USERS_FILE));
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java
new file mode 100644
index 0000000..5aec37f
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/PropertiesLoginModuleTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class PropertiesLoginModuleTest extends Assert {
+
+   static {
+      String path = System.getProperty("java.security.auth.login.config");
+      if (path == null) {
+         URL resource = PropertiesLoginModuleTest.class.getClassLoader().getResource("login.config");
+         if (resource != null) {
+            path = resource.getFile();
+            System.setProperty("java.security.auth.login.config", path);
+         }
+      }
+   }
+
+   @Test
+   public void testLogin() throws LoginException {
+      LoginContext context = new LoginContext("PropertiesLogin", new CallbackHandler() {
+         public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+               if (callbacks[i] instanceof NameCallback) {
+                  ((NameCallback) callbacks[i]).setName("first");
+               }
+               else if (callbacks[i] instanceof PasswordCallback) {
+                  ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray());
+               }
+               else {
+                  throw new UnsupportedCallbackException(callbacks[i]);
+               }
+            }
+         }
+      });
+      context.login();
+
+      Subject subject = context.getSubject();
+
+      assertEquals("Should have three principals", 3, subject.getPrincipals().size());
+      assertEquals("Should have one user principal", 1, subject.getPrincipals(UserPrincipal.class).size());
+      assertEquals("Should have two group principals", 2, subject.getPrincipals(RolePrincipal.class).size());
+
+      context.logout();
+
+      assertEquals("Should have zero principals", 0, subject.getPrincipals().size());
+   }
+
+   @Test
+   public void testBadUseridLogin() throws Exception {
+      LoginContext context = new LoginContext("PropertiesLogin", new CallbackHandler() {
+         public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+               if (callbacks[i] instanceof NameCallback) {
+                  ((NameCallback) callbacks[i]).setName("BAD");
+               }
+               else if (callbacks[i] instanceof PasswordCallback) {
+                  ((PasswordCallback) callbacks[i]).setPassword("secret".toCharArray());
+               }
+               else {
+                  throw new UnsupportedCallbackException(callbacks[i]);
+               }
+            }
+         }
+      });
+      try {
+         context.login();
+         fail("Should have thrown a FailedLoginException");
+      }
+      catch (FailedLoginException doNothing) {
+      }
+
+   }
+
+   @Test
+   public void testBadPWLogin() throws Exception {
+      LoginContext context = new LoginContext("PropertiesLogin", new CallbackHandler() {
+         public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+            for (int i = 0; i < callbacks.length; i++) {
+               if (callbacks[i] instanceof NameCallback) {
+                  ((NameCallback) callbacks[i]).setName("first");
+               }
+               else if (callbacks[i] instanceof PasswordCallback) {
+                  ((PasswordCallback) callbacks[i]).setPassword("BAD".toCharArray());
+               }
+               else {
+                  throw new UnsupportedCallbackException(callbacks[i]);
+               }
+            }
+         }
+      });
+      try {
+         context.login();
+         fail("Should have thrown a FailedLoginException");
+      }
+      catch (FailedLoginException doNothing) {
+      }
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/RolePrincipalTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/RolePrincipalTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/RolePrincipalTest.java
new file mode 100644
index 0000000..e23fe6a
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/RolePrincipalTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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 org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class RolePrincipalTest extends Assert {
+
+   @Test
+   public void testArguments() {
+      RolePrincipal principal = new RolePrincipal("FOO");
+
+      assertEquals("FOO", principal.getName());
+
+      try {
+         new RolePrincipal(null);
+         fail("Should have thrown IllegalArgumentException");
+      }
+      catch (IllegalArgumentException ingore) {
+
+      }
+   }
+
+   @Test
+   public void testHash() {
+      RolePrincipal p1 = new RolePrincipal("FOO");
+      RolePrincipal p2 = new RolePrincipal("FOO");
+
+      assertEquals(p1.hashCode(), p1.hashCode());
+      assertEquals(p1.hashCode(), p2.hashCode());
+   }
+
+   @Test
+   public void testEquals() {
+      RolePrincipal p1 = new RolePrincipal("FOO");
+      RolePrincipal p2 = new RolePrincipal("FOO");
+      RolePrincipal p3 = new RolePrincipal("BAR");
+
+      assertTrue(p1.equals(p1));
+      assertTrue(p1.equals(p2));
+      assertFalse(p1.equals(null));
+      assertFalse(p1.equals("FOO"));
+      assertFalse(p1.equals(p3));
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/StubCertificateLoginModule.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/StubCertificateLoginModule.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/StubCertificateLoginModule.java
new file mode 100644
index 0000000..6c7bf24
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/StubCertificateLoginModule.java
@@ -0,0 +1,47 @@
+/*
+ * 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.auth.login.LoginException;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+
+import org.apache.activemq.artemis.spi.core.security.jaas.CertificateLoginModule;
+
+public class StubCertificateLoginModule extends CertificateLoginModule {
+
+   final String userName;
+   final Set groupNames;
+
+   String lastUserName;
+   X509Certificate[] lastCertChain;
+
+   public StubCertificateLoginModule(String userName, Set groupNames) {
+      this.userName = userName;
+      this.groupNames = groupNames;
+   }
+
+   protected String getUserNameForCertificates(X509Certificate[] certs) throws LoginException {
+      lastCertChain = certs;
+      return userName;
+   }
+
+   protected Set getUserGroups(String username) throws LoginException {
+      lastUserName = username;
+      return this.groupNames;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/UserPrincipalTest.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/UserPrincipalTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/UserPrincipalTest.java
new file mode 100644
index 0000000..9a08141
--- /dev/null
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/UserPrincipalTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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 org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class UserPrincipalTest extends Assert {
+
+   @Test
+   public void testArguments() {
+      UserPrincipal principal = new UserPrincipal("FOO");
+
+      assertEquals("FOO", principal.getName());
+
+      try {
+         new UserPrincipal(null);
+         fail("Should have thrown IllegalArgumentException");
+      }
+      catch (IllegalArgumentException ingore) {
+
+      }
+   }
+
+   @Test
+   public void testHash() {
+      UserPrincipal p1 = new UserPrincipal("FOO");
+      UserPrincipal p2 = new UserPrincipal("FOO");
+
+      assertEquals(p1.hashCode(), p1.hashCode());
+      assertEquals(p1.hashCode(), p2.hashCode());
+   }
+
+   @Test
+   public void testEquals() {
+      UserPrincipal p1 = new UserPrincipal("FOO");
+      UserPrincipal p2 = new UserPrincipal("FOO");
+      UserPrincipal p3 = new UserPrincipal("BAR");
+
+      assertTrue(p1.equals(p1));
+      assertTrue(p1.equals(p2));
+      assertFalse(p1.equals(null));
+      assertFalse(p1.equals("FOO"));
+      assertFalse(p1.equals(p3));
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java b/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java
index 715094c..7cbe2f0 100644
--- a/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/tests/util/ActiveMQTestBase.java
@@ -793,20 +793,24 @@ public abstract class ActiveMQTestBase extends Assert {
    }
 
    protected final void clearDataRecreateServerDirs() {
-      clearDataRecreateServerDirs(getTestDir());
+      clearDataRecreateServerDirs(0, false);
    }
 
-   protected void clearDataRecreateServerDirs(final String testDir1) {
+   protected final void clearDataRecreateServerDirs(int index, boolean backup) {
+      clearDataRecreateServerDirs(getTestDir(), index, backup);
+   }
+
+   protected void clearDataRecreateServerDirs(final String testDir1, int index, boolean backup) {
       // Need to delete the root
 
       File file = new File(testDir1);
       deleteDirectory(file);
       file.mkdirs();
 
-      recreateDirectory(getJournalDir(testDir1));
-      recreateDirectory(getBindingsDir(testDir1));
-      recreateDirectory(getPageDir(testDir1));
-      recreateDirectory(getLargeMessagesDir(testDir1));
+      recreateDirectory(getJournalDir(testDir1, index, backup));
+      recreateDirectory(getBindingsDir(testDir1, index, backup));
+      recreateDirectory(getPageDir(testDir1, index, backup));
+      recreateDirectory(getLargeMessagesDir(testDir1, index, backup));
       recreateDirectory(getClientLargeMessagesDir(testDir1));
       recreateDirectory(getTemporaryDir(testDir1));
    }
@@ -815,7 +819,7 @@ public abstract class ActiveMQTestBase extends Assert {
     * @return the journalDir
     */
    public String getJournalDir() {
-      return getJournalDir(getTestDir());
+      return getJournalDir(0, false);
    }
 
    protected static String getJournalDir(final String testDir1) {
@@ -834,7 +838,7 @@ public abstract class ActiveMQTestBase extends Assert {
     * @return the bindingsDir
     */
    protected String getBindingsDir() {
-      return getBindingsDir(getTestDir());
+      return getBindingsDir(0, false);
    }
 
    /**
@@ -859,7 +863,7 @@ public abstract class ActiveMQTestBase extends Assert {
     * @return the pageDir
     */
    protected String getPageDir() {
-      return getPageDir(getTestDir());
+      return getPageDir(0, false);
    }
 
    protected File getPageDirFile() {
@@ -886,7 +890,7 @@ public abstract class ActiveMQTestBase extends Assert {
     * @return the largeMessagesDir
     */
    protected String getLargeMessagesDir() {
-      return getLargeMessagesDir(getTestDir());
+      return getLargeMessagesDir(0, false);
    }
 
    /**

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/resources/login.config
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/resources/login.config b/artemis-server/src/test/resources/login.config
new file mode 100644
index 0000000..9b1e1c0
--- /dev/null
+++ b/artemis-server/src/test/resources/login.config
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+PropertiesLogin {
+    org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule required
+        debug=true
+        org.apache.activemq.jaas.properties.user="users.properties"
+        org.apache.activemq.jaas.properties.role="roles.properties";
+};
+
+LDAPLogin {
+    org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required
+        debug=true
+        initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
+        connectionURL="ldap://localhost:1024"
+        connectionUsername="uid=admin,ou=system"
+        connectionPassword=secret
+        connectionProtocol=s
+        authentication=simple
+        userBase="ou=system"
+        userSearchMatching="(uid={0})"
+        userSearchSubtree=false
+        roleBase="ou=system"
+        roleName=cn
+        roleSearchMatching="(member=uid={1},ou=system)"
+        roleSearchSubtree=false
+        ;
+};
+
+UnAuthenticatedLDAPLogin {
+    org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required
+        debug=true
+        initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
+        connectionURL="ldap://localhost:1024"
+        connectionUsername="uid=admin,ou=system"
+        connectionPassword=""
+        connectionProtocol=s
+        authentication=simple
+        userBase="ou=system"
+        userSearchMatching="(uid={0})"
+        userSearchSubtree=false
+        roleBase="ou=system"
+        roleName=dummyRoleName
+        roleSearchMatching="(uid={1})"
+        roleSearchSubtree=false
+        ;
+};
+
+ExpandedLDAPLogin {
+    org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required
+        debug=true
+        initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
+        connectionURL="ldap://localhost:1024"
+        connectionUsername="uid=admin,ou=system"
+        connectionPassword=secret
+        connectionProtocol=s
+        authentication=simple
+        userBase="ou=system"
+        userSearchMatching="(uid={0})"
+        userSearchSubtree=false
+        roleBase="ou=system"
+        roleName=cn
+        roleSearchMatching="(uid={1})"
+        roleSearchSubtree=false
+               expandRoles=true
+               expandRolesMatching="(member={0})"
+        ;
+};
+
+GuestLogin {
+    org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule required
+        debug=true
+        org.apache.activemq.jaas.guest.user="foo"
+        org.apache.activemq.jaas.guest.role="bar";
+
+};
+
+GuestLoginWithDefaults {
+    org.apache.activemq.artemis.spi.core.security.jaas.GuestLoginModule required
+        debug=true;
+};
+
+OpenLdapConfiguration {
+  org.apache.activemq.artemis.spi.core.security.jaas.LDAPLoginModule required
+        debug=true
+        initialContextFactory=com.sun.jndi.ldap.LdapCtxFactory
+        connectionURL="ldap://localhost:389"
+        connectionUsername="cn=mqbroker,ou=Services,ou=system,dc=fusesource,dc=com"
+        connectionPassword="sunflower"
+        connectionProtocol="s"
+        topicSearchMatchingFormat="cn={0},ou=Topic,ou=Destination,ou=ActiveMQ,ou=system,dc=fusesource,dc=com"
+        topicSearchSubtreeBool=true
+        authentication=simple
+        userBase="ou=User,ou=ActiveMQ,ou=system,dc=fusesource,dc=com"
+        userSearchMatching="(uid={0})"
+        userSearchSubtree=false
+        roleSearchMatching="(uid={1})"
+        queueSearchMatchingFormat="cn={0},ou=Queue,ou=Destination,ou=ActiveMQ,ou=system,dc=fusesource,dc=com"
+        queueSearchSubtreeBool=true
+        roleBase="ou=Group,ou=ActiveMQ,ou=system,dc=fusesource,dc=com"
+        roleName=cn
+        roleSearchMatching="(member:=uid={1})"
+        roleSearchSubtree=true
+        ;
+};

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/resources/roles.properties
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/resources/roles.properties b/artemis-server/src/test/resources/roles.properties
new file mode 100644
index 0000000..de332d3
--- /dev/null
+++ b/artemis-server/src/test/resources/roles.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+
+programmers=first
+accounting=second
+employees=first,second

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/resources/test.ldif
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/resources/test.ldif b/artemis-server/src/test/resources/test.ldif
new file mode 100644
index 0000000..6d6bd58
--- /dev/null
+++ b/artemis-server/src/test/resources/test.ldif
@@ -0,0 +1,39 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+
+dn: uid=first,ou=system
+uid: first
+userPassword: secret
+objectClass: account
+objectClass: simpleSecurityObject
+objectClass: top
+
+###################
+## Define groups ##
+###################
+
+dn: cn=admins,ou=system
+cn: admins
+member: uid=first,ou=system
+objectClass: groupOfNames
+objectClass: top
+
+dn: cn=users,ou=system
+cn: users
+member: cn=admins,ou=system
+objectClass: groupOfNames
+objectClass: top
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/test/resources/users.properties
----------------------------------------------------------------------
diff --git a/artemis-server/src/test/resources/users.properties b/artemis-server/src/test/resources/users.properties
new file mode 100644
index 0000000..1087b0b
--- /dev/null
+++ b/artemis-server/src/test/resources/users.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=secret
+second=password


Mime
View raw message