activemq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From clebertsuco...@apache.org
Subject [4/5] activemq-artemis git commit: ARTEMIS-74 import JAAS auth from 5.x
Date Fri, 09 Oct 2015 19:58:12 GMT
ARTEMIS-74 import JAAS auth from 5.x

This change allows the use of JAAS login modules for basic authentication
and authorization.


Project: http://git-wip-us.apache.org/repos/asf/activemq-artemis/repo
Commit: http://git-wip-us.apache.org/repos/asf/activemq-artemis/commit/6ed9c5ae
Tree: http://git-wip-us.apache.org/repos/asf/activemq-artemis/tree/6ed9c5ae
Diff: http://git-wip-us.apache.org/repos/asf/activemq-artemis/diff/6ed9c5ae

Branch: refs/heads/master
Commit: 6ed9c5ae91dc7a08cdb3825fb17a5da24037fa36
Parents: e971f11
Author: jbertram <jbertram@apache.org>
Authored: Thu Oct 1 16:58:26 2015 -0500
Committer: jbertram <jbertram@apache.org>
Committed: Fri Oct 9 11:42:22 2015 -0500

----------------------------------------------------------------------
 .../activemq/artemis/cli/commands/Create.java   |  53 +-
 .../artemis/factory/JaasSecurityHandler.java    |  32 ++
 .../artemis/broker/security/jaas-security       |  17 +
 .../commands/etc/artemis-roles-basic.properties |  17 +
 .../commands/etc/artemis-roles-jaas.properties  |  17 +
 .../cli/commands/etc/artemis-roles.properties   |  17 -
 .../artemis/cli/commands/etc/artemis.profile    |   2 +-
 .../cli/commands/etc/artemis.profile.cmd        |   2 +-
 .../etc/basic-broker-security-settings.txt      |   5 +
 .../artemis/cli/commands/etc/bootstrap.xml      |   5 +-
 .../etc/jaas-broker-security-settings.txt       |   2 +
 .../artemis/cli/commands/etc/login.config       |  22 +
 .../activemq/cli/test/StreamClassPathTest.java  |   5 +-
 .../activemq/artemis/dto/JaasSecurityDTO.java   |  30 ++
 .../org/apache/activemq/artemis/dto/jaxb.index  |   7 +-
 .../artemis/maven/ArtemisCreatePlugin.java      |   5 +-
 artemis-server/pom.xml                          |  18 +
 .../security/ActiveMQJAASSecurityManager.java   | 114 +++++
 .../core/security/ActiveMQSecurityManager2.java |   2 +-
 .../spi/core/security/JAASSecurityManager.java  | 216 --------
 .../core/security/jaas/CertificateCallback.java |  48 ++
 .../security/jaas/CertificateLoginModule.java   | 183 +++++++
 .../core/security/jaas/GuestLoginModule.java    | 132 +++++
 .../jaas/JaasCertificateCallbackHandler.java    |  65 +++
 .../jaas/JaasCredentialCallbackHandler.java     |  63 +++
 .../spi/core/security/jaas/LDAPLoginModule.java | 505 +++++++++++++++++++
 .../core/security/jaas/LDAPLoginProperty.java   |  41 ++
 .../core/security/jaas/PrincipalProperties.java |  75 +++
 .../security/jaas/PropertiesLoginModule.java    | 215 ++++++++
 .../spi/core/security/jaas/RolePrincipal.java   |  68 +++
 .../jaas/TextFileCertificateLoginModule.java    | 147 ++++++
 .../spi/core/security/jaas/UserPrincipal.java   |  68 +++
 .../jaas/CertificateLoginModuleTest.java        | 154 ++++++
 .../security/jaas/GuestLoginModuleTest.java     |  91 ++++
 .../core/security/jaas/LDAPLoginModuleTest.java | 149 ++++++
 .../jaas/LDAPModuleRoleExpansionTest.java       | 136 +++++
 .../PropertiesLoginModuleRaceConditionTest.java | 195 +++++++
 .../jaas/PropertiesLoginModuleTest.java         | 130 +++++
 .../core/security/jaas/RolePrincipalTest.java   |  61 +++
 .../jaas/StubCertificateLoginModule.java        |  47 ++
 .../core/security/jaas/UserPrincipalTest.java   |  61 +++
 .../artemis/tests/util/ActiveMQTestBase.java    |  24 +-
 artemis-server/src/test/resources/login.config  | 118 +++++
 .../src/test/resources/roles.properties         |  20 +
 artemis-server/src/test/resources/test.ldif     |  39 ++
 .../src/test/resources/users.properties         |  19 +
 docs/user-manual/en/security.md                 | 288 ++++++++++-
 examples/features/standard/pom.xml              |   2 +
 .../features/standard/security-jaas/pom.xml     | 111 ++++
 .../features/standard/security-jaas/readme.html | 324 ++++++++++++
 .../jms/example/JaasSecurityExample.java        | 282 +++++++++++
 .../activemq/server0/artemis-roles.properties   |  20 +
 .../activemq/server0/artemis-users.properties   |  20 +
 .../main/resources/activemq/server0/broker.xml  |  81 +++
 .../src/main/resources/jndi.properties          |  22 +
 pom.xml                                         |   2 +
 tests/integration-tests/pom.xml                 |  18 +
 .../integration/security/LDAPSecurityTest.java  | 347 +++++++++++++
 .../integration/security/SecurityTest.java      | 314 +++++++++++-
 .../src/test/resources/login.config             | 118 +++++
 .../src/test/resources/roles.properties         |  20 +
 .../src/test/resources/test.ldif                |  39 ++
 .../src/test/resources/users.properties         |  19 +
 63 files changed, 5186 insertions(+), 283 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
index 0a430a3..0c558f8 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Create.java
@@ -70,8 +70,16 @@ public class Create extends InputAbstract {
    public static final String ETC_LOGGING_PROPERTIES = "etc/logging.properties";
    public static final String ETC_BOOTSTRAP_XML = "etc/bootstrap.xml";
    public static final String ETC_BROKER_XML = "etc/broker.xml";
+
+   // The JAAS PropertiesLogin module uses role=user(s) syntax, but the basic security uses user=role(s) syntax so we need 2 different files here
    public static final String ETC_ARTEMIS_ROLES_PROPERTIES = "etc/artemis-roles.properties";
+   public static final String ETC_ARTEMIS_ROLES_BASIC_PROPERTIES = "etc/artemis-roles-basic.properties";
+   public static final String ETC_ARTEMIS_ROLES_JAAS_PROPERTIES = "etc/artemis-roles-jaas.properties";
+
    public static final String ETC_ARTEMIS_USERS_PROPERTIES = "etc/artemis-users.properties";
+   public static final String ETC_JAAS_BROKER_SECURITY_SETTINGS_TXT = "etc/jaas-broker-security-settings.txt";
+   public static final String ETC_BASIC_BROKER_SECURITY_SETTINGS_TXT = "etc/basic-broker-security-settings.txt";
+   public static final String ETC_LOGIN_CONFIG = "etc/login.config";
    public static final String ETC_REPLICATED_SETTINGS_TXT = "etc/replicated-settings.txt";
    public static final String ETC_SHARED_STORE_SETTINGS_TXT = "etc/shared-store-settings.txt";
    public static final String ETC_CLUSTER_SECURITY_SETTINGS_TXT = "etc/cluster-security-settings.txt";
@@ -161,10 +169,24 @@ public class Create extends InputAbstract {
    @Option(name = "--aio", description = "Force aio journal on the configuration regardless of the library being available or not.")
    boolean forceLibaio;
 
+   @Option(name = "--broker-security", description = "Use basic, file-based security or JAAS login module for broker security (Default: basic)")
+   String brokerSecurity;
+
    boolean IS_WINDOWS;
 
    boolean IS_CYGWIN;
 
+   public String getBrokerSecurity() {
+      if (brokerSecurity == null) {
+         brokerSecurity = "basic";
+      }
+      return brokerSecurity;
+   }
+
+   public void setBrokerSecurity(String security) {
+      this.brokerSecurity = security;
+   }
+
    public int getMaxHops() {
       return maxHops;
    }
@@ -535,6 +557,29 @@ public class Create extends InputAbstract {
 
       filters.put("${java-opts}", javaOptions);
 
+      if (isAllowAnonymous()) {
+         filters.put("${bootstrap.guest}", "default-user=\"" + getUser() + "\"");
+      }
+      else {
+         filters.put("${bootstrap.guest}", "");
+      }
+
+      if (brokerSecurity != null && brokerSecurity.equalsIgnoreCase("jaas")) {
+         filters.put("${broker-security-settings}", applyFilters(readTextFile(ETC_JAAS_BROKER_SECURITY_SETTINGS_TXT), filters));
+         filters.put("${login-config}", "-Djava.security.auth.login.config=" + path(directory, false) + "/etc/login.config");
+         write(ETC_LOGIN_CONFIG, filters, false);
+         write(ETC_ARTEMIS_ROLES_JAAS_PROPERTIES, filters, false);
+         File file = new File(directory, ETC_ARTEMIS_ROLES_JAAS_PROPERTIES);
+         file.renameTo(new File(directory, ETC_ARTEMIS_ROLES_PROPERTIES));
+      }
+      else {
+         filters.put("${broker-security-settings}", applyFilters(readTextFile(ETC_BASIC_BROKER_SECURITY_SETTINGS_TXT), filters));
+         filters.put("${login-config}", "");
+         write(ETC_ARTEMIS_ROLES_BASIC_PROPERTIES, filters, false);
+         File file = new File(directory, ETC_ARTEMIS_ROLES_BASIC_PROPERTIES);
+         file.renameTo(new File(directory, ETC_ARTEMIS_ROLES_PROPERTIES));
+      }
+
       if (IS_WINDOWS) {
          write(BIN_ARTEMIS_CMD, null, false);
          write(BIN_ARTEMIS_SERVICE_EXE);
@@ -553,13 +598,6 @@ public class Create extends InputAbstract {
 
       write(ETC_LOGGING_PROPERTIES, null, false);
 
-      if (isAllowAnonymous()) {
-         filters.put("${bootstrap.guest}", "default-user=\"" + getUser() + "\"");
-      }
-      else {
-         filters.put("${bootstrap.guest}", "");
-      }
-
       if (noWeb) {
          filters.put("${bootstrap-web-settings}", "");
       }
@@ -571,7 +609,6 @@ public class Create extends InputAbstract {
 
       write(ETC_BOOTSTRAP_XML, filters, false);
       write(ETC_BROKER_XML, filters, false);
-      write(ETC_ARTEMIS_ROLES_PROPERTIES, filters, false);
       write(ETC_ARTEMIS_USERS_PROPERTIES, filters, false);
 
       context.out.println("");

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/java/org/apache/activemq/artemis/factory/JaasSecurityHandler.java
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/factory/JaasSecurityHandler.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/factory/JaasSecurityHandler.java
new file mode 100644
index 0000000..2cd1785
--- /dev/null
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/factory/JaasSecurityHandler.java
@@ -0,0 +1,32 @@
+/*
+ * 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.factory;
+
+import org.apache.activemq.artemis.dto.JaasSecurityDTO;
+import org.apache.activemq.artemis.dto.SecurityDTO;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
+
+public class JaasSecurityHandler implements SecurityHandler {
+   @Override
+   public ActiveMQSecurityManager createSecurityManager(SecurityDTO security) throws Exception {
+      JaasSecurityDTO jaasSecurity = (JaasSecurityDTO) security;
+      ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager();
+      securityManager.setConfigurationName(jaasSecurity.loginModule);
+      return securityManager;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/META-INF/services/org/apache/activemq/artemis/broker/security/jaas-security
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/META-INF/services/org/apache/activemq/artemis/broker/security/jaas-security b/artemis-cli/src/main/resources/META-INF/services/org/apache/activemq/artemis/broker/security/jaas-security
new file mode 100644
index 0000000..013a63c
--- /dev/null
+++ b/artemis-cli/src/main/resources/META-INF/services/org/apache/activemq/artemis/broker/security/jaas-security
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+class=org.apache.activemq.artemis.factory.JaasSecurityHandler

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles-basic.properties
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles-basic.properties b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles-basic.properties
new file mode 100644
index 0000000..04c3c4c
--- /dev/null
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles-basic.properties
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+${user}=${role}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles-jaas.properties
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles-jaas.properties b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles-jaas.properties
new file mode 100644
index 0000000..c9443dd
--- /dev/null
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles-jaas.properties
@@ -0,0 +1,17 @@
+## ---------------------------------------------------------------------------
+## 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.
+## ---------------------------------------------------------------------------
+${role}=${user}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles.properties
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles.properties b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles.properties
deleted file mode 100644
index 04c3c4c..0000000
--- a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis-roles.properties
+++ /dev/null
@@ -1,17 +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.
-## ---------------------------------------------------------------------------
-${user}=${role}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile
index 2a51e2a..76ca12f 100644
--- a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile
@@ -23,7 +23,7 @@ ARTEMIS_HOME='${artemis.home}'
 
 
 # Java Opts
-JAVA_ARGS="-XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods -Xms512M -Xmx1024M -Xbootclasspath/a:$ARTEMIS_HOME/lib/${logmanager} -Djava.util.logging.manager=org.jboss.logmanager.LogManager ${java-opts}"
+JAVA_ARGS="-XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods -Xms512M -Xmx1024M -Xbootclasspath/a:$ARTEMIS_HOME/lib/${logmanager} -Djava.util.logging.manager=org.jboss.logmanager.LogManager ${login-config} ${java-opts}"
 
 
 # Debug args: Uncomment to enable debug

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile.cmd
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile.cmd b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile.cmd
index c52d70f..835c7b7 100644
--- a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile.cmd
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/artemis.profile.cmd
@@ -21,7 +21,7 @@ rem Cluster Properties: Used to pass arguments to ActiveMQ Artemis which can be
 rem set ARTEMIS_CLUSTER_PROPS=-Dactivemq.remoting.default.port=61617 -Dactivemq.remoting.amqp.port=5673 -Dactivemq.remoting.stomp.port=61614 -Dactivemq.remoting.hornetq.port=5446
 
 rem Java Opts
-set JAVA_ARGS=-XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods -Xms512M -Xmx1024M ${java-opts}
+set JAVA_ARGS=-XX:+UseParallelGC -XX:+AggressiveOpts -XX:+UseFastAccessorMethods -Xms512M -Xmx1024M ${login-config} ${java-opts}
 
 rem Debug args: Uncomment to enable debug
 rem set DEBUG_ARGS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/basic-broker-security-settings.txt
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/basic-broker-security-settings.txt b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/basic-broker-security-settings.txt
new file mode 100644
index 0000000..dd0a5f1
--- /dev/null
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/basic-broker-security-settings.txt
@@ -0,0 +1,5 @@
+
+   <basic-security
+           users="file:${artemis.instance}/etc/artemis-users.properties"
+           roles="file:${artemis.instance}/etc/artemis-roles.properties"
+           ${bootstrap.guest}/>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap.xml
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap.xml b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap.xml
index be51734..fe3f864 100644
--- a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap.xml
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/bootstrap.xml
@@ -18,10 +18,7 @@
 
 <broker xmlns="http://activemq.org/schema">
 
-   <basic-security
-           users="file:${artemis.instance}/etc/artemis-users.properties"
-           roles="file:${artemis.instance}/etc/artemis-roles.properties"
-           ${bootstrap.guest}/>
+${broker-security-settings}
 
    <server configuration="file:${artemis.instance}/etc/broker.xml"/>
 

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/jaas-broker-security-settings.txt
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/jaas-broker-security-settings.txt b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/jaas-broker-security-settings.txt
new file mode 100644
index 0000000..6521bf4
--- /dev/null
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/jaas-broker-security-settings.txt
@@ -0,0 +1,2 @@
+
+   <jaas-security login-module="PropertiesLogin"/>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/login.config
----------------------------------------------------------------------
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/login.config b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/login.config
new file mode 100644
index 0000000..fe8ca36
--- /dev/null
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/login.config
@@ -0,0 +1,22 @@
+/*
+ * 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="artemis-users.properties"
+        org.apache.activemq.jaas.properties.role="artemis-roles.properties";
+};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-cli/src/test/java/org/apache/activemq/cli/test/StreamClassPathTest.java
----------------------------------------------------------------------
diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/StreamClassPathTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/StreamClassPathTest.java
index 21579dc..e1d045d 100644
--- a/artemis-cli/src/test/java/org/apache/activemq/cli/test/StreamClassPathTest.java
+++ b/artemis-cli/src/test/java/org/apache/activemq/cli/test/StreamClassPathTest.java
@@ -40,7 +40,8 @@ public class StreamClassPathTest {
       openStream(Create.ETC_LOGGING_PROPERTIES);
       openStream(Create.ETC_BOOTSTRAP_XML);
       openStream(Create.ETC_BROKER_XML);
-      openStream(Create.ETC_ARTEMIS_ROLES_PROPERTIES);
+      openStream(Create.ETC_ARTEMIS_ROLES_BASIC_PROPERTIES);
+      openStream(Create.ETC_ARTEMIS_ROLES_JAAS_PROPERTIES);
       openStream(Create.ETC_ARTEMIS_USERS_PROPERTIES);
       openStream(Create.ETC_REPLICATED_SETTINGS_TXT);
       openStream(Create.ETC_REPLICATED_SETTINGS_TXT);
@@ -50,6 +51,8 @@ public class StreamClassPathTest {
       openStream(Create.ETC_CONNECTOR_SETTINGS_TXT);
       openStream(Create.ETC_BOOTSTRAP_WEB_SETTINGS_TXT);
       openStream(Create.ETC_JOURNAL_BUFFER_SETTINGS);
+      openStream(Create.ETC_JAAS_BROKER_SECURITY_SETTINGS_TXT);
+      openStream(Create.ETC_BASIC_BROKER_SECURITY_SETTINGS_TXT);
    }
 
    private void openStream(String source) throws Exception {

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/JaasSecurityDTO.java
----------------------------------------------------------------------
diff --git a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/JaasSecurityDTO.java b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/JaasSecurityDTO.java
new file mode 100644
index 0000000..99163cf
--- /dev/null
+++ b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/JaasSecurityDTO.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dto;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+
+@XmlRootElement(name = "jaas-security")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class JaasSecurityDTO extends SecurityDTO {
+
+   @XmlAttribute(name = "login-module", required = true)
+   public String loginModule;
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-dto/src/main/resources/org/apache/activemq/artemis/dto/jaxb.index
----------------------------------------------------------------------
diff --git a/artemis-dto/src/main/resources/org/apache/activemq/artemis/dto/jaxb.index b/artemis-dto/src/main/resources/org/apache/activemq/artemis/dto/jaxb.index
index 5803f73..b0bacd7 100644
--- a/artemis-dto/src/main/resources/org/apache/activemq/artemis/dto/jaxb.index
+++ b/artemis-dto/src/main/resources/org/apache/activemq/artemis/dto/jaxb.index
@@ -2,19 +2,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 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 
+## See the License for the specific language governing permissions and
 ## limitations under the License.
 ## ---------------------------------------------------------------------------
 BrokerDTO
 SecurityDTO
 BasicSecurityDTO
+JaasSecurityDTO
 

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java
----------------------------------------------------------------------
diff --git a/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java b/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java
index 39b6d8e..ba6cb8e 100644
--- a/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java
+++ b/artemis-maven-plugin/src/main/java/org/apache/activemq/artemis/maven/ArtemisCreatePlugin.java
@@ -113,6 +113,9 @@ public class ArtemisCreatePlugin extends ArtemisAbstractPlugin {
    @Parameter(defaultValue = "ON_DEMAND")
    private String messageLoadBalancing;
 
+   @Parameter(defaultValue = "basic")
+   private String brokerSecurity;
+
    /**
     * For extra stuff not covered by the properties
     */
@@ -200,7 +203,7 @@ public class ArtemisCreatePlugin extends ArtemisAbstractPlugin {
 
       ArrayList<String> listCommands = new ArrayList<>();
 
-      add(listCommands, "create", "--allow-anonymous", "--silent", "--force", "--no-web", "--user", user, "--password", password, "--role", role, "--port-offset", "" + portOffset, "--data", dataFolder);
+      add(listCommands, "create", "--allow-anonymous", "--silent", "--force", "--no-web", "--user", user, "--password", password, "--role", role, "--port-offset", "" + portOffset, "--data", dataFolder, "--broker-security", brokerSecurity);
 
       if (allowAnonymous) {
          add(listCommands, "--allow-anonymous");

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/pom.xml
----------------------------------------------------------------------
diff --git a/artemis-server/pom.xml b/artemis-server/pom.xml
index 652ef81..36cef29 100644
--- a/artemis-server/pom.xml
+++ b/artemis-server/pom.xml
@@ -82,6 +82,24 @@
          <artifactId>junit</artifactId>
          <scope>test</scope>
       </dependency>
+      <dependency>
+         <groupId>org.apache.directory.server</groupId>
+         <artifactId>apacheds-server-integ</artifactId>
+         <version>${directory-version}</version>
+         <scope>test</scope>
+      </dependency>
+      <dependency>
+         <groupId>org.apache.directory.server</groupId>
+         <artifactId>apacheds-core-integ</artifactId>
+         <version>${directory-version}</version>
+         <scope>test</scope>
+         <exclusions>
+            <exclusion>
+               <groupId>bouncycastle</groupId>
+               <artifactId>bcprov-jdk15</artifactId>
+            </exclusion>
+         </exclusions>
+      </dependency>
    </dependencies>
 
    <profiles>

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java
new file mode 100644
index 0000000..6a13f22
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import javax.security.auth.Subject;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.activemq.artemis.core.security.CheckType;
+import org.apache.activemq.artemis.core.security.Role;
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+import org.apache.activemq.artemis.spi.core.security.jaas.JaasCredentialCallbackHandler;
+import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+
+/**
+ * This implementation delegates to the JAAS security interfaces.
+ *
+ * The {@link Subject} returned by the login context is expecting to have a set of {@link RolePrincipal} for each
+ * role of the user.
+ */
+public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager {
+
+   private final boolean trace = ActiveMQServerLogger.LOGGER.isTraceEnabled();
+
+   private String configurationName;
+
+   public boolean validateUser(final String user, final String password) {
+      try {
+         getAuthenticatedSubject(user, password);
+         return true;
+      }
+      catch (LoginException e) {
+         ActiveMQServerLogger.LOGGER.debug("Couldn't validate user: " + user, e);
+         return false;
+      }
+   }
+
+   public boolean validateUserAndRole(final String user,
+                                      final String password,
+                                      final Set<Role> roles,
+                                      final CheckType checkType) {
+      Subject localSubject;
+      try {
+         localSubject = getAuthenticatedSubject(user, password);
+      }
+      catch (LoginException e) {
+         ActiveMQServerLogger.LOGGER.debug("Couldn't validate user: " + user, e);
+         return false;
+      }
+
+      boolean authorized = false;
+
+      if (localSubject != null) {
+         Set<RolePrincipal> rolesWithPermission = getPrincipalsInRole(checkType, roles);
+
+         // Check the caller's roles
+         Set<RolePrincipal> rolesForSubject = localSubject.getPrincipals(RolePrincipal.class);
+         if (rolesForSubject.size() > 0 && rolesWithPermission.size() > 0) {
+            Iterator<RolePrincipal> rolesForSubjectIter = rolesForSubject.iterator();
+            while (!authorized && rolesForSubjectIter.hasNext()) {
+               Iterator<RolePrincipal> rolesWithPermissionIter = rolesWithPermission.iterator();
+               while (!authorized && rolesWithPermissionIter.hasNext()) {
+                  Principal role = rolesWithPermissionIter.next();
+                  authorized = rolesForSubjectIter.next().equals(role);
+               }
+            }
+         }
+
+         if (trace) {
+            ActiveMQServerLogger.LOGGER.trace("user " + user + (authorized ? " is " : " is NOT ") + "authorized");
+         }
+      }
+
+      return authorized;
+   }
+
+   private Subject getAuthenticatedSubject(final String user, final String password) throws LoginException {
+      LoginContext lc = new LoginContext(configurationName, new JaasCredentialCallbackHandler(user, password));
+      lc.login();
+      return lc.getSubject();
+   }
+
+   private Set<RolePrincipal> getPrincipalsInRole(final CheckType checkType, final Set<Role> roles) {
+      Set<RolePrincipal> principals = new HashSet<>();
+      for (Role role : roles) {
+         if (checkType.hasRole(role)) {
+            principals.add(new RolePrincipal(role.getName()));
+         }
+      }
+      return principals;
+   }
+
+   public void setConfigurationName(final String configurationName) {
+      this.configurationName = configurationName;
+   }
+}

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager2.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager2.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager2.java
index 2962153..1e3cb10 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager2.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager2.java
@@ -46,4 +46,4 @@ public interface ActiveMQSecurityManager2 extends ActiveMQSecurityManager {
     * @return true if the user is valid and they have the correct roles for the given destination address
     */
    boolean validateUserAndRole(String user, String password, Set<Role> roles, CheckType checkType, String address);
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/activemq-artemis/blob/6ed9c5ae/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/JAASSecurityManager.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/JAASSecurityManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/JAASSecurityManager.java
deleted file mode 100644
index 48699b6..0000000
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/JAASSecurityManager.java
+++ /dev/null
@@ -1,216 +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;
-
-import java.security.Principal;
-import java.security.acl.Group;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
-
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.Configuration;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.apache.activemq.artemis.core.security.CheckType;
-import org.apache.activemq.artemis.core.security.Role;
-import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
-
-/**
- * This implementation delegates to the JAAS security interfaces.
- *
- * The {@link Subject} returned by the login context is expecting to have a {@link Group} with the <code>Roles</code> name
- * containing a set of {@link Principal} for each role of the user.
- */
-public class JAASSecurityManager implements ActiveMQSecurityManager {
-   // Static --------------------------------------------------------
-
-   // Attributes ----------------------------------------------------
-
-   private final boolean trace = ActiveMQServerLogger.LOGGER.isTraceEnabled();
-
-   private String configurationName;
-
-   private CallbackHandler callbackHandler;
-
-   private Configuration config;
-
-   // ActiveMQSecurityManager implementation -----------------------------
-
-   public boolean validateUser(final String user, final String password) {
-      try {
-         getAuthenticatedSubject(user, password);
-         return true;
-      }
-      catch (LoginException e1) {
-         return false;
-      }
-   }
-
-   public boolean validateUserAndRole(final String user,
-                                      final String password,
-                                      final Set<Role> roles,
-                                      final CheckType checkType) {
-      Subject localSubject = null;
-      try {
-         localSubject = getAuthenticatedSubject(user, password);
-      }
-      catch (LoginException e1) {
-         return false;
-      }
-
-      boolean authenticated = true;
-
-      if (localSubject != null) {
-         Set<Principal> rolePrincipals = getRolePrincipals(checkType, roles);
-
-         // authenticated = realmMapping.doesUserHaveRole(principal, rolePrincipals);
-
-         boolean hasRole = false;
-         // check that the caller is authenticated to the current thread
-
-         // Check the caller's roles
-         Group subjectRoles = getSubjectRoles(localSubject);
-         if (subjectRoles != null) {
-            Iterator<Principal> iter = rolePrincipals.iterator();
-            while (!hasRole && iter.hasNext()) {
-               Principal role = iter.next();
-               hasRole = subjectRoles.isMember(role);
-            }
-         }
-
-         authenticated = hasRole;
-
-         if (trace) {
-            ActiveMQServerLogger.LOGGER.trace("user " + user + (authenticated ? " is " : " is NOT ") + "authorized");
-         }
-      }
-      return authenticated;
-   }
-
-   private Subject getAuthenticatedSubject(final String user, final String password) throws LoginException {
-      SimplePrincipal principal = user == null ? null : new SimplePrincipal(user);
-
-      char[] passwordChars = null;
-
-      if (password != null) {
-         passwordChars = password.toCharArray();
-      }
-
-      Subject subject = new Subject();
-
-      if (user != null) {
-         subject.getPrincipals().add(principal);
-      }
-      subject.getPrivateCredentials().add(passwordChars);
-
-      LoginContext lc = new LoginContext(configurationName, subject, callbackHandler, config);
-      lc.login();
-      return lc.getSubject();
-   }
-
-   private Group getSubjectRoles(final Subject subject) {
-      Set<Group> subjectGroups = subject.getPrincipals(Group.class);
-      Iterator<Group> iter = subjectGroups.iterator();
-      Group roles = null;
-      while (iter.hasNext()) {
-         Group grp = iter.next();
-         String name = grp.getName();
-         if (name.equals("Roles")) {
-            roles = grp;
-         }
-      }
-      return roles;
-   }
-
-   private Set<Principal> getRolePrincipals(final CheckType checkType, final Set<Role> roles) {
-      Set<Principal> principals = new HashSet<Principal>();
-      for (Role role : roles) {
-         if (checkType.hasRole(role)) {
-            principals.add(new SimplePrincipal(role.getName()));
-         }
-      }
-      return principals;
-   }
-
-   // Public --------------------------------------------------------
-
-   public void setConfigurationName(final String configurationName) {
-      this.configurationName = configurationName;
-   }
-
-   public void setCallbackHandler(final CallbackHandler handler) {
-      callbackHandler = handler;
-   }
-
-   public void setConfiguration(final Configuration config) {
-      this.config = config;
-   }
-
-   // Private -------------------------------------------------------
-
-   // Inner classes -------------------------------------------------
-
-   public static class SimplePrincipal implements Principal, java.io.Serializable {
-
-      private static final long serialVersionUID = 1L;
-
-      private final String name;
-
-      public SimplePrincipal(final String name) {
-         this.name = name;
-      }
-
-      /**
-       * Compare this SimplePrincipal's name against another Principal
-       *
-       * @return true if name equals another.getName();
-       */
-      @Override
-      public boolean equals(final Object another) {
-         if (!(another instanceof Principal)) {
-            return false;
-         }
-         String anotherName = ((Principal) another).getName();
-         boolean equals = false;
-         if (name == null) {
-            equals = anotherName == null;
-         }
-         else {
-            equals = name.equals(anotherName);
-         }
-         return equals;
-      }
-
-      @Override
-      public int hashCode() {
-         return name == null ? 0 : name.hashCode();
-      }
-
-      @Override
-      public String toString() {
-         return name;
-      }
-
-      public String getName() {
-         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/CertificateCallback.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateCallback.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateCallback.java
new file mode 100644
index 0000000..630dd32
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateCallback.java
@@ -0,0 +1,48 @@
+/*
+ * 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.callback.Callback;
+import java.security.cert.X509Certificate;
+
+/**
+ * A Callback for SSL certificates.
+ *
+ * Will return a certificate chain to its client.
+ */
+public class CertificateCallback implements Callback {
+
+   X509Certificate[] certificates;
+
+   /**
+    * Setter for certificate chain.
+    *
+    * @param certs The certificates to be returned.
+    */
+   public void setCertificates(X509Certificate[] certs) {
+      certificates = certs;
+   }
+
+   /**
+    * Getter for certificate chain.
+    *
+    * @return The certificates being carried.
+    */
+   public X509Certificate[] getCertificates() {
+      return certificates;
+   }
+}

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/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
new file mode 100644
index 0000000..db8808b
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/CertificateLoginModule.java
@@ -0,0 +1,183 @@
+/*
+ * 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.UnsupportedCallbackException;
+import javax.security.auth.login.FailedLoginException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+import java.io.IOException;
+import java.security.Principal;
+import java.security.cert.X509Certificate;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+
+/**
+ * A LoginModule that allows for authentication based on SSL certificates.
+ * Allows for subclasses to define methods used to verify user certificates and
+ * find user groups. Uses CertificateCallbacks to retrieve certificates.
+ */
+public abstract class CertificateLoginModule implements LoginModule {
+
+   private CallbackHandler callbackHandler;
+   private Subject subject;
+
+   private X509Certificate[] certificates;
+   private String username;
+   private Set<String> groups;
+   private Set<Principal> principals = new HashSet<Principal>();
+   private boolean debug;
+
+   /**
+    * Overriding to allow for proper initialization. Standard JAAS.
+    */
+   @Override
+   public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
+      this.subject = subject;
+      this.callbackHandler = callbackHandler;
+
+      debug = "true".equalsIgnoreCase((String) options.get("debug"));
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("Initialized debug");
+      }
+   }
+
+   /**
+    * Overriding to allow for certificate-based login. Standard JAAS.
+    */
+   @Override
+   public boolean login() throws LoginException {
+      Callback[] callbacks = new Callback[1];
+
+      callbacks[0] = new CertificateCallback();
+      try {
+         callbackHandler.handle(callbacks);
+      }
+      catch (IOException ioe) {
+         throw new LoginException(ioe.getMessage());
+      }
+      catch (UnsupportedCallbackException uce) {
+         throw new LoginException(uce.getMessage() + " Unable to obtain client certificates.");
+      }
+      certificates = ((CertificateCallback) callbacks[0]).getCertificates();
+
+      username = getUserNameForCertificates(certificates);
+      if (username == null) {
+         throw new FailedLoginException("No user for client certificate: " + getDistinguishedName(certificates));
+      }
+
+      groups = getUserGroups(username);
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("Certificate for user: " + username);
+      }
+      return true;
+   }
+
+   /**
+    * Overriding to complete login process. Standard JAAS.
+    */
+   @Override
+   public boolean commit() throws LoginException {
+      principals.add(new UserPrincipal(username));
+
+      for (String group : groups) {
+         principals.add(new RolePrincipal(group));
+      }
+
+      subject.getPrincipals().addAll(principals);
+
+      clear();
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("commit");
+      }
+      return true;
+   }
+
+   /**
+    * Standard JAAS override.
+    */
+   @Override
+   public boolean abort() throws LoginException {
+      clear();
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("abort");
+      }
+      return true;
+   }
+
+   /**
+    * Standard JAAS override.
+    */
+   @Override
+   public boolean logout() {
+      subject.getPrincipals().removeAll(principals);
+      principals.clear();
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("logout");
+      }
+      return true;
+   }
+
+   /**
+    * Helper method.
+    */
+   private void clear() {
+      groups.clear();
+      certificates = null;
+   }
+
+   /**
+    * Should return a unique name corresponding to the certificates given. The
+    * name returned will be used to look up access levels as well as group
+    * associations.
+    *
+    * @param certs The distinguished name.
+    * @return The unique name if the certificate is recognized, null otherwise.
+    */
+   protected abstract String getUserNameForCertificates(final X509Certificate[] certs) throws LoginException;
+
+   /**
+    * Should return a set of the groups this user belongs to. The groups
+    * returned will be added to the user's credentials.
+    *
+    * @param username The username of the client. This is the same name that
+    *                 getUserNameForDn returned for the user's DN.
+    * @return A Set of the names of the groups this user belongs to.
+    */
+   protected abstract Set<String> getUserGroups(final String username) throws LoginException;
+
+   protected String getDistinguishedName(final X509Certificate[] certs) {
+      if (certs != null && certs.length > 0 && certs[0] != null) {
+         return certs[0].getSubjectDN().getName();
+      }
+      else {
+         return 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/GuestLoginModule.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java
new file mode 100644
index 0000000..dbea86b
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/GuestLoginModule.java
@@ -0,0 +1,132 @@
+/*
+ * 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.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+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;
+
+/**
+ * Always login the user with a default 'guest' identity.
+ *
+ * Useful for unauthenticated communication channels being used in the
+ * same broker as authenticated ones.
+ *
+ */
+public class GuestLoginModule implements LoginModule {
+
+   private static final String GUEST_USER = "org.apache.activemq.jaas.guest.user";
+   private static final String GUEST_ROLE = "org.apache.activemq.jaas.guest.role";
+
+   private String userName = "guest";
+   private String roleName = "guests";
+   private Subject subject;
+   private boolean debug;
+   private boolean credentialsInvalidate;
+   private Set<Principal> principals = new HashSet<Principal>();
+   private CallbackHandler callbackHandler;
+   private boolean loginSucceeded;
+
+   @Override
+   public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
+      this.subject = subject;
+      this.callbackHandler = callbackHandler;
+      debug = "true".equalsIgnoreCase((String) options.get("debug"));
+      credentialsInvalidate = "true".equalsIgnoreCase((String) options.get("credentialsInvalidate"));
+      if (options.get(GUEST_USER) != null) {
+         userName = (String) options.get(GUEST_USER);
+      }
+      if (options.get(GUEST_ROLE) != null) {
+         roleName = (String) options.get(GUEST_ROLE);
+      }
+      principals.add(new UserPrincipal(userName));
+      principals.add(new RolePrincipal(roleName));
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("Initialized debug=" + debug + " guestUser=" + userName + " guestGroup=" + roleName);
+      }
+
+   }
+
+   @Override
+   public boolean login() throws LoginException {
+      loginSucceeded = true;
+      if (credentialsInvalidate) {
+         PasswordCallback passwordCallback = new PasswordCallback("Password: ", false);
+         try {
+            callbackHandler.handle(new Callback[]{passwordCallback});
+            if (passwordCallback.getPassword() != null) {
+               if (debug) {
+                  ActiveMQServerLogger.LOGGER.debug("Guest login failing (credentialsInvalidate=true) on presence of a password");
+               }
+               loginSucceeded = false;
+               passwordCallback.clearPassword();
+            }
+         }
+         catch (IOException ioe) {
+         }
+         catch (UnsupportedCallbackException uce) {
+         }
+      }
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("Guest login " + loginSucceeded);
+      }
+      return loginSucceeded;
+   }
+
+   @Override
+   public boolean commit() throws LoginException {
+      if (loginSucceeded) {
+         subject.getPrincipals().addAll(principals);
+      }
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("commit");
+      }
+      return loginSucceeded;
+   }
+
+   @Override
+   public boolean abort() throws LoginException {
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("abort");
+      }
+      return true;
+   }
+
+   @Override
+   public boolean logout() throws LoginException {
+      subject.getPrincipals().removeAll(principals);
+
+      if (debug) {
+         ActiveMQServerLogger.LOGGER.debug("logout");
+      }
+      return true;
+   }
+}

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/JaasCertificateCallbackHandler.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCertificateCallbackHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCertificateCallbackHandler.java
new file mode 100644
index 0000000..b53f946
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCertificateCallbackHandler.java
@@ -0,0 +1,65 @@
+/*
+ * 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.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import java.io.IOException;
+import java.security.cert.X509Certificate;
+
+/**
+ * A Standard JAAS callback handler for SSL certificate requests. Will only
+ * handle callbacks of type CertificateCallback.
+ */
+public class JaasCertificateCallbackHandler implements CallbackHandler {
+
+   final X509Certificate[] certificates;
+
+   /**
+    * Basic constructor.
+    *
+    * @param certs The certificate returned when calling back.
+    */
+   public JaasCertificateCallbackHandler(X509Certificate[] certs) {
+      certificates = certs;
+   }
+
+   /**
+    * Overriding handle method to handle certificates.
+    *
+    * @param callbacks The callbacks requested.
+    * @throws IOException
+    * @throws UnsupportedCallbackException Thrown if an unkown Callback type is
+    *                                      encountered.
+    */
+   @Override
+   public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+      for (int i = 0; i < callbacks.length; i++) {
+         Callback callback = callbacks[i];
+         if (callback instanceof CertificateCallback) {
+            CertificateCallback certCallback = (CertificateCallback) callback;
+
+            certCallback.setCertificates(certificates);
+
+         }
+         else {
+            throw new UnsupportedCallbackException(callback);
+         }
+      }
+   }
+}

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/JaasCredentialCallbackHandler.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCredentialCallbackHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCredentialCallbackHandler.java
new file mode 100644
index 0000000..34ae701
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/JaasCredentialCallbackHandler.java
@@ -0,0 +1,63 @@
+/*
+ * 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.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 java.io.IOException;
+
+/**
+ * A JAAS username password CallbackHandler.
+ */
+public class JaasCredentialCallbackHandler implements CallbackHandler {
+
+   private final String username;
+   private final String password;
+
+   public JaasCredentialCallbackHandler(String username, String password) {
+      this.username = username;
+      this.password = password;
+   }
+
+   @Override
+   public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+      for (int i = 0; i < callbacks.length; i++) {
+         Callback callback = callbacks[i];
+         if (callback instanceof PasswordCallback) {
+            PasswordCallback passwordCallback = (PasswordCallback) callback;
+            if (password == null) {
+               passwordCallback.setPassword(null);
+            }
+            else {
+               passwordCallback.setPassword(password.toCharArray());
+            }
+         }
+         else if (callback instanceof NameCallback) {
+            NameCallback nameCallback = (NameCallback) callback;
+            if (username == null) {
+               nameCallback.setName(null);
+            }
+            else {
+               nameCallback.setName(username);
+            }
+         }
+      }
+   }
+}

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/LDAPLoginModule.java
----------------------------------------------------------------------
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java
new file mode 100644
index 0000000..6830828
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/LDAPLoginModule.java
@@ -0,0 +1,505 @@
+/*
+ * 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.naming.AuthenticationException;
+import javax.naming.CommunicationException;
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.Attribute;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InitialDirContext;
+import javax.naming.directory.SearchControls;
+import javax.naming.directory.SearchResult;
+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.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.Principal;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+
+public class LDAPLoginModule implements LoginModule {
+
+   private static final String INITIAL_CONTEXT_FACTORY = "initialContextFactory";
+   private static final String CONNECTION_URL = "connectionURL";
+   private static final String CONNECTION_USERNAME = "connectionUsername";
+   private static final String CONNECTION_PASSWORD = "connectionPassword";
+   private static final String CONNECTION_PROTOCOL = "connectionProtocol";
+   private static final String AUTHENTICATION = "authentication";
+   private static final String USER_BASE = "userBase";
+   private static final String USER_SEARCH_MATCHING = "userSearchMatching";
+   private static final String USER_SEARCH_SUBTREE = "userSearchSubtree";
+   private static final String ROLE_BASE = "roleBase";
+   private static final String ROLE_NAME = "roleName";
+   private static final String ROLE_SEARCH_MATCHING = "roleSearchMatching";
+   private static final String ROLE_SEARCH_SUBTREE = "roleSearchSubtree";
+   private static final String USER_ROLE_NAME = "userRoleName";
+   private static final String EXPAND_ROLES = "expandRoles";
+   private static final String EXPAND_ROLES_MATCHING = "expandRolesMatching";
+
+   protected DirContext context;
+
+   private Subject subject;
+   private CallbackHandler handler;
+   private LDAPLoginProperty[] config;
+   private String username;
+   private Set<RolePrincipal> groups = new HashSet<RolePrincipal>();
+
+   @Override
+   public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
+      this.subject = subject;
+      this.handler = callbackHandler;
+
+      config = new LDAPLoginProperty[]{new LDAPLoginProperty(INITIAL_CONTEXT_FACTORY, (String) options.get(INITIAL_CONTEXT_FACTORY)), new LDAPLoginProperty(CONNECTION_URL, (String) options.get(CONNECTION_URL)), new LDAPLoginProperty(CONNECTION_USERNAME, (String) options.get(CONNECTION_USERNAME)), new LDAPLoginProperty(CONNECTION_PASSWORD, (String) options.get(CONNECTION_PASSWORD)), new LDAPLoginProperty(CONNECTION_PROTOCOL, (String) options.get(CONNECTION_PROTOCOL)), new LDAPLoginProperty(AUTHENTICATION, (String) options.get(AUTHENTICATION)), new LDAPLoginProperty(USER_BASE, (String) options.get(USER_BASE)), new LDAPLoginProperty(USER_SEARCH_MATCHING, (String) options.get(USER_SEARCH_MATCHING)), new LDAPLoginProperty(USER_SEARCH_SUBTREE, (String) options.get(USER_SEARCH_SUBTREE)), new LDAPLoginProperty(ROLE_BASE, (String) options.get(ROLE_BASE)), new LDAPLoginProperty(ROLE_NAME, (String) options.get(ROLE_NAME)), new LDAPLoginProperty(ROLE_SEARCH_MATCHING, (String) options.get(ROLE_S
 EARCH_MATCHING)), new LDAPLoginProperty(ROLE_SEARCH_SUBTREE, (String) options.get(ROLE_SEARCH_SUBTREE)), new LDAPLoginProperty(USER_ROLE_NAME, (String) options.get(USER_ROLE_NAME)), new LDAPLoginProperty(EXPAND_ROLES, (String) options.get(EXPAND_ROLES)), new LDAPLoginProperty(EXPAND_ROLES_MATCHING, (String) options.get(EXPAND_ROLES_MATCHING))};
+   }
+
+   @Override
+   public boolean login() throws LoginException {
+
+      Callback[] callbacks = new Callback[2];
+
+      callbacks[0] = new NameCallback("User name");
+      callbacks[1] = new PasswordCallback("Password", false);
+      try {
+         handler.handle(callbacks);
+      }
+      catch (IOException ioe) {
+         throw (LoginException) new LoginException().initCause(ioe);
+      }
+      catch (UnsupportedCallbackException uce) {
+         throw (LoginException) new LoginException().initCause(uce);
+      }
+
+      String password;
+
+      username = ((NameCallback) callbacks[0]).getName();
+      if (username == null)
+         return false;
+
+      if (((PasswordCallback) callbacks[1]).getPassword() != null)
+         password = new String(((PasswordCallback) callbacks[1]).getPassword());
+      else
+         password = "";
+
+      // authenticate will throw LoginException
+      // in case of failed authentication
+      authenticate(username, password);
+      return true;
+   }
+
+   @Override
+   public boolean logout() throws LoginException {
+      username = null;
+      return true;
+   }
+
+   @Override
+   public boolean commit() throws LoginException {
+      Set<Principal> principals = subject.getPrincipals();
+      principals.add(new UserPrincipal(username));
+      for (RolePrincipal gp : groups) {
+         principals.add(gp);
+      }
+      return true;
+   }
+
+   @Override
+   public boolean abort() throws LoginException {
+      username = null;
+      return true;
+   }
+
+   protected void close(DirContext context) {
+      try {
+         context.close();
+      }
+      catch (Exception e) {
+         ActiveMQServerLogger.LOGGER.error(e.toString());
+      }
+   }
+
+   protected boolean authenticate(String username, String password) throws LoginException {
+
+      MessageFormat userSearchMatchingFormat;
+      boolean userSearchSubtreeBool;
+
+      DirContext context = null;
+
+      if (ActiveMQServerLogger.LOGGER.isDebugEnabled()) {
+         ActiveMQServerLogger.LOGGER.debug("Create the LDAP initial context.");
+      }
+      try {
+         context = open();
+      }
+      catch (NamingException ne) {
+         FailedLoginException ex = new FailedLoginException("Error opening LDAP connection");
+         ex.initCause(ne);
+         throw ex;
+      }
+
+      if (!isLoginPropertySet(USER_SEARCH_MATCHING))
+         return false;
+
+      userSearchMatchingFormat = new MessageFormat(getLDAPPropertyValue(USER_SEARCH_MATCHING));
+      userSearchSubtreeBool = Boolean.valueOf(getLDAPPropertyValue(USER_SEARCH_SUBTREE)).booleanValue();
+
+      try {
+
+         String filter = userSearchMatchingFormat.format(new String[]{doRFC2254Encoding(username)});
+         SearchControls constraints = new SearchControls();
+         if (userSearchSubtreeBool) {
+            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
+         }
+         else {
+            constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
+         }
+
+         // setup attributes
+         List<String> list = new ArrayList<String>();
+         if (isLoginPropertySet(USER_ROLE_NAME)) {
+            list.add(getLDAPPropertyValue(USER_ROLE_NAME));
+         }
+         String[] attribs = new String[list.size()];
+         list.toArray(attribs);
+         constraints.setReturningAttributes(attribs);
+
+         if (ActiveMQServerLogger.LOGGER.isDebugEnabled()) {
+            ActiveMQServerLogger.LOGGER.debug("Get the user DN.");
+            ActiveMQServerLogger.LOGGER.debug("Looking for the user in LDAP with ");
+            ActiveMQServerLogger.LOGGER.debug("  base DN: " + getLDAPPropertyValue(USER_BASE));
+            ActiveMQServerLogger.LOGGER.debug("  filter: " + filter);
+         }
+
+         NamingEnumeration<SearchResult> results = context.search(getLDAPPropertyValue(USER_BASE), filter, constraints);
+
+         if (results == null || !results.hasMore()) {
+            ActiveMQServerLogger.LOGGER.warn("User " + username + " not found in LDAP.");
+            throw new FailedLoginException("User " + username + " not found in LDAP.");
+         }
+
+         SearchResult result = results.next();
+
+         if (results.hasMore()) {
+            // ignore for now
+         }
+
+         String dn;
+         if (result.isRelative()) {
+            ActiveMQServerLogger.LOGGER.debug("LDAP returned a relative name: " + result.getName());
+
+            NameParser parser = context.getNameParser("");
+            Name contextName = parser.parse(context.getNameInNamespace());
+            Name baseName = parser.parse(getLDAPPropertyValue(USER_BASE));
+            Name entryName = parser.parse(result.getName());
+            Name name = contextName.addAll(baseName);
+            name = name.addAll(entryName);
+            dn = name.toString();
+         }
+         else {
+            ActiveMQServerLogger.LOGGER.debug("LDAP returned an absolute name: " + result.getName());
+
+            try {
+               URI uri = new URI(result.getName());
+               String path = uri.getPath();
+
+               if (path.startsWith("/")) {
+                  dn = path.substring(1);
+               }
+               else {
+                  dn = path;
+               }
+            }
+            catch (URISyntaxException e) {
+               if (context != null) {
+                  close(context);
+               }
+               FailedLoginException ex = new FailedLoginException("Error parsing absolute name as URI.");
+               ex.initCause(e);
+               throw ex;
+            }
+         }
+
+         if (ActiveMQServerLogger.LOGGER.isDebugEnabled()) {
+            ActiveMQServerLogger.LOGGER.debug("Using DN [" + dn + "] for binding.");
+         }
+
+         Attributes attrs = result.getAttributes();
+         if (attrs == null) {
+            throw new FailedLoginException("User found, but LDAP entry malformed: " + username);
+         }
+         List<String> roles = null;
+         if (isLoginPropertySet(USER_ROLE_NAME)) {
+            roles = addAttributeValues(getLDAPPropertyValue(USER_ROLE_NAME), attrs, roles);
+         }
+
+         // check the credentials by binding to server
+         if (bindUser(context, dn, password)) {
+            // if authenticated add more roles
+            roles = getRoles(context, dn, username, roles);
+            if (ActiveMQServerLogger.LOGGER.isDebugEnabled()) {
+               ActiveMQServerLogger.LOGGER.debug("Roles " + roles + " for user " + username);
+            }
+            for (int i = 0; i < roles.size(); i++) {
+               groups.add(new RolePrincipal(roles.get(i)));
+            }
+         }
+         else {
+            throw new FailedLoginException("Password does not match for user: " + username);
+         }
+      }
+      catch (CommunicationException e) {
+         FailedLoginException ex = new FailedLoginException("Error contacting LDAP");
+         ex.initCause(e);
+         throw ex;
+      }
+      catch (NamingException e) {
+         if (context != null) {
+            close(context);
+         }
+         FailedLoginException ex = new FailedLoginException("Error contacting LDAP");
+         ex.initCause(e);
+         throw ex;
+      }
+
+      return true;
+   }
+
+   protected List<String> getRoles(DirContext context,
+                                   String dn,
+                                   String username,
+                                   List<String> currentRoles) throws NamingException {
+      List<String> list = currentRoles;
+      MessageFormat roleSearchMatchingFormat;
+      boolean roleSearchSubtreeBool;
+      boolean expandRolesBool;
+      roleSearchMatchingFormat = new MessageFormat(getLDAPPropertyValue(ROLE_SEARCH_MATCHING));
+      roleSearchSubtreeBool = Boolean.valueOf(getLDAPPropertyValue(ROLE_SEARCH_SUBTREE)).booleanValue();
+      expandRolesBool = Boolean.valueOf(getLDAPPropertyValue(EXPAND_ROLES)).booleanValue();
+
+      if (list == null) {
+         list = new ArrayList<String>();
+      }
+      if (!isLoginPropertySet(ROLE_NAME)) {
+         return list;
+      }
+      String filter = roleSearchMatchingFormat.format(new String[]{doRFC2254Encoding(dn), doRFC2254Encoding(username)});
+
+      SearchControls constraints = new SearchControls();
+      if (roleSearchSubtreeBool) {
+         constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
+      }
+      else {
+         constraints.setSearchScope(SearchControls.ONELEVEL_SCOPE);
+      }
+      if (ActiveMQServerLogger.LOGGER.isDebugEnabled()) {
+         ActiveMQServerLogger.LOGGER.debug("Get user roles.");
+         ActiveMQServerLogger.LOGGER.debug("Looking for the user roles in LDAP with ");
+         ActiveMQServerLogger.LOGGER.debug("  base DN: " + getLDAPPropertyValue(ROLE_BASE));
+         ActiveMQServerLogger.LOGGER.debug("  filter: " + filter);
+      }
+      HashSet<String> haveSeenNames = new HashSet<String>();
+      Queue<String> pendingNameExpansion = new LinkedList<String>();
+      NamingEnumeration<SearchResult> results = context.search(getLDAPPropertyValue(ROLE_BASE), filter, constraints);
+      while (results.hasMore()) {
+         SearchResult result = results.next();
+         Attributes attrs = result.getAttributes();
+         if (expandRolesBool) {
+            haveSeenNames.add(result.getNameInNamespace());
+            pendingNameExpansion.add(result.getNameInNamespace());
+         }
+         if (attrs == null) {
+            continue;
+         }
+         list = addAttributeValues(getLDAPPropertyValue(ROLE_NAME), attrs, list);
+      }
+      if (expandRolesBool) {
+         MessageFormat expandRolesMatchingFormat = new MessageFormat(getLDAPPropertyValue(EXPAND_ROLES_MATCHING));
+         while (!pendingNameExpansion.isEmpty()) {
+            String name = pendingNameExpansion.remove();
+            filter = expandRolesMatchingFormat.format(new String[]{name});
+            results = context.search(getLDAPPropertyValue(ROLE_BASE), filter, constraints);
+            while (results.hasMore()) {
+               SearchResult result = results.next();
+               name = result.getNameInNamespace();
+               if (!haveSeenNames.contains(name)) {
+                  Attributes attrs = result.getAttributes();
+                  list = addAttributeValues(getLDAPPropertyValue(ROLE_NAME), attrs, list);
+                  haveSeenNames.add(name);
+                  pendingNameExpansion.add(name);
+               }
+            }
+         }
+      }
+      return list;
+   }
+
+   protected String doRFC2254Encoding(String inputString) {
+      StringBuffer buf = new StringBuffer(inputString.length());
+      for (int i = 0; i < inputString.length(); i++) {
+         char c = inputString.charAt(i);
+         switch (c) {
+            case '\\':
+               buf.append("\\5c");
+               break;
+            case '*':
+               buf.append("\\2a");
+               break;
+            case '(':
+               buf.append("\\28");
+               break;
+            case ')':
+               buf.append("\\29");
+               break;
+            case '\0':
+               buf.append("\\00");
+               break;
+            default:
+               buf.append(c);
+               break;
+         }
+      }
+      return buf.toString();
+   }
+
+   protected boolean bindUser(DirContext context, String dn, String password) throws NamingException {
+      boolean isValid = false;
+
+      if (ActiveMQServerLogger.LOGGER.isDebugEnabled()) {
+         ActiveMQServerLogger.LOGGER.debug("Binding the user.");
+      }
+      context.addToEnvironment(Context.SECURITY_PRINCIPAL, dn);
+      context.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
+      try {
+         context.getAttributes("", null);
+         isValid = true;
+         if (ActiveMQServerLogger.LOGGER.isDebugEnabled()) {
+            ActiveMQServerLogger.LOGGER.debug("User " + dn + " successfully bound.");
+         }
+      }
+      catch (AuthenticationException e) {
+         isValid = false;
+         if (ActiveMQServerLogger.LOGGER.isDebugEnabled()) {
+            ActiveMQServerLogger.LOGGER.debug("Authentication failed for dn=" + dn);
+         }
+      }
+
+      if (isLoginPropertySet(CONNECTION_USERNAME)) {
+         context.addToEnvironment(Context.SECURITY_PRINCIPAL, getLDAPPropertyValue(CONNECTION_USERNAME));
+      }
+      else {
+         context.removeFromEnvironment(Context.SECURITY_PRINCIPAL);
+      }
+      if (isLoginPropertySet(CONNECTION_PASSWORD)) {
+         context.addToEnvironment(Context.SECURITY_CREDENTIALS, getLDAPPropertyValue(CONNECTION_PASSWORD));
+      }
+      else {
+         context.removeFromEnvironment(Context.SECURITY_CREDENTIALS);
+      }
+
+      return isValid;
+   }
+
+   private List<String> addAttributeValues(String attrId,
+                                           Attributes attrs,
+                                           List<String> values) throws NamingException {
+
+      if (attrId == null || attrs == null) {
+         return values;
+      }
+      if (values == null) {
+         values = new ArrayList<String>();
+      }
+      Attribute attr = attrs.get(attrId);
+      if (attr == null) {
+         return values;
+      }
+      NamingEnumeration<?> e = attr.getAll();
+      while (e.hasMore()) {
+         String value = (String) e.next();
+         values.add(value);
+      }
+      return values;
+   }
+
+   protected DirContext open() throws NamingException {
+      try {
+         Hashtable<String, String> env = new Hashtable<String, String>();
+         env.put(Context.INITIAL_CONTEXT_FACTORY, getLDAPPropertyValue(INITIAL_CONTEXT_FACTORY));
+         if (isLoginPropertySet(CONNECTION_USERNAME)) {
+            env.put(Context.SECURITY_PRINCIPAL, getLDAPPropertyValue(CONNECTION_USERNAME));
+         }
+         else {
+            throw new NamingException("Empty username is not allowed");
+         }
+
+         if (isLoginPropertySet(CONNECTION_PASSWORD)) {
+            env.put(Context.SECURITY_CREDENTIALS, getLDAPPropertyValue(CONNECTION_PASSWORD));
+         }
+         else {
+            throw new NamingException("Empty password is not allowed");
+         }
+         env.put(Context.SECURITY_PROTOCOL, getLDAPPropertyValue(CONNECTION_PROTOCOL));
+         env.put(Context.PROVIDER_URL, getLDAPPropertyValue(CONNECTION_URL));
+         env.put(Context.SECURITY_AUTHENTICATION, getLDAPPropertyValue(AUTHENTICATION));
+         context = new InitialDirContext(env);
+
+      }
+      catch (NamingException e) {
+         ActiveMQServerLogger.LOGGER.error(e.toString());
+         throw e;
+      }
+      return context;
+   }
+
+   private String getLDAPPropertyValue(String propertyName) {
+      for (int i = 0; i < config.length; i++)
+         if (config[i].getPropertyName() == propertyName)
+            return config[i].getPropertyValue();
+      return null;
+   }
+
+   private boolean isLoginPropertySet(String propertyName) {
+      for (int i = 0; i < config.length; i++) {
+         if (config[i].getPropertyName() == propertyName && (config[i].getPropertyValue() != null && !"".equals(config[i].getPropertyValue())))
+            return true;
+      }
+      return false;
+   }
+
+}


Mime
View raw message