[activemq-artemis] branch master updated: ARTEMIS-2947 Implement SecurityManager that supports replication

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[activemq-artemis] branch master updated: ARTEMIS-2947 Implement SecurityManager that supports replication

clebertsuconic-2
This is an automated email from the ASF dual-hosted git repository.

clebertsuconic pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/activemq-artemis.git


The following commit(s) were added to refs/heads/master by this push:
     new 75e12b5  ARTEMIS-2947 Implement SecurityManager that supports replication
     new 0be78c8  This closes #3300
75e12b5 is described below

commit 75e12b5e1d4d9e2322b84fea0c0a1e81981200ae
Author: Justin Bertram <[hidden email]>
AuthorDate: Mon Oct 12 14:40:36 2020 -0500

    ARTEMIS-2947 Implement SecurityManager that supports replication
---
 .../activemq/artemis/cli/commands/Create.java      |  26 ++
 .../apache/activemq/artemis/cli/commands/Run.java  |  10 +-
 .../artemis/cli/factory/jmx/ManagementFactory.java |   4 +-
 .../cli/commands/etc/basic-security-manager.txt    |   5 +
 .../artemis/cli/commands/etc/bootstrap.xml         |   2 +-
 .../cli/commands/etc/jaas-security-manager.txt     |   1 +
 .../org/apache/activemq/cli/test/ArtemisTest.java  | 177 ++++++++----
 .../artemis/utils/PasswordMaskingUtil.java         |  14 +-
 .../api/core/management/ActiveMQServerControl.java |  18 +-
 .../activemq/artemis/dto/JMXConnectorDTO.java      |   2 +-
 .../apache/activemq/artemis/dto/WebServerDTO.java  |   2 +-
 .../management/impl/ActiveMQServerControlImpl.java | 124 ++++----
 .../artemis/core/persistence/StorageManager.java   |  23 +-
 .../core/persistence/config/PersistedRole.java     | 108 +++++++
 ...tedRoles.java => PersistedSecuritySetting.java} |  30 +-
 .../core/persistence/config/PersistedUser.java     |  86 ++++++
 .../journal/AbstractJournalStorageManager.java     | 120 +++++++-
 .../persistence/impl/journal/DescribeJournal.java  |  12 +-
 .../persistence/impl/journal/JournalRecordIds.java |   6 +-
 .../impl/nullpm/NullStorageManager.java            |  36 ++-
 .../activemq/artemis/core/security/User.java       |  11 +-
 .../artemis/core/server/ActiveMQMessageBundle.java |   3 +
 .../artemis/core/server/ActiveMQServer.java        |   4 +
 .../artemis/core/server/ActiveMQServerLogger.java  |   8 +
 .../core/server/impl/ActiveMQServerImpl.java       |  28 +-
 .../core/server/management/BasicAuthenticator.java |  56 ++++
 .../server/management/ManagementConnector.java     |  23 +-
 .../core/server/management/ManagementContext.java  |  16 +-
 .../security/ActiveMQBasicSecurityManager.java     | 211 ++++++++++++++
 .../core/security/ActiveMQJAASSecurityManager.java | 108 +------
 .../artemis/spi/core/security/UserManagement.java  |  31 ++
 .../spi/core/security/jaas/LDAPLoginModule.java    |   2 +-
 .../jaas/PropertiesLoginModuleConfigurator.java    |  11 +-
 .../artemis/utils/SecurityManagerUtil.java         | 138 +++++++++
 .../core/transaction/impl/TransactionImplTest.java |  40 ++-
 docs/user-manual/en/security.md                    | 101 ++++++-
 .../tests/integration/client/SendAckFailTest.java  |  46 ++-
 .../persistence/RolesConfigurationStorageTest.java |  24 +-
 .../security/BasicSecurityManagerFailoverTest.java | 164 +++++++++++
 .../security/BasicSecurityManagerTest.java         | 312 +++++++++++++++++++++
 40 files changed, 1833 insertions(+), 310 deletions(-)

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 5cb6e8b..aaaad9b 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
@@ -107,6 +107,8 @@ public class Create extends InputAbstract {
    public static final String ETC_PING_TXT = "etc/ping-settings.txt";
    public static final String ETC_COMMENTED_PING_TXT = "etc/commented-ping-settings.txt";
    public static final String ETC_DATABASE_STORE_TXT = "etc/database-store.txt";
+   public static final String ETC_JAAS_SECURITY_MANAGER_TXT = "etc/jaas-security-manager.txt";
+   public static final String ETC_BASIC_SECURITY_MANAGER_TXT = "etc/basic-security-manager.txt";
 
    public static final String ETC_GLOBAL_MAX_SPECIFIED_TXT = "etc/global-max-specified.txt";
    public static final String ETC_GLOBAL_MAX_DEFAULT_TXT = "etc/global-max-default.txt";
@@ -289,6 +291,9 @@ public class Create extends InputAbstract {
       }
    }
 
+   @Option(name = "--security-manager", description = "Which security manager to use - jaas or basic (Default: jaas)")
+   private String securityManager = "jaas";
+
    @Option(name = "--jdbc-bindings-table-name", description = "Name of the jdbc bindigns table")
    private String jdbcBindings = ActiveMQDefaultConfiguration.getDefaultBindingsTableName();
 
@@ -821,6 +826,11 @@ public class Create extends InputAbstract {
 
       // we want this variable to remain unchanged so that it will use the value set in the profile
       filters.remove("${artemis.instance}");
+      if (SecurityManagerType.getType(securityManager) == SecurityManagerType.BASIC) {
+         filters.put("${security-manager-settings}", readTextFile(ETC_BASIC_SECURITY_MANAGER_TXT, filters));
+      } else {
+         filters.put("${security-manager-settings}", readTextFile(ETC_JAAS_SECURITY_MANAGER_TXT, filters));
+      }
       writeEtc(ETC_BOOTSTRAP_XML, etcFolder, filters, false);
       writeEtc(ETC_MANAGEMENT_XML, etcFolder, filters, false);
 
@@ -1193,4 +1203,20 @@ public class Create extends InputAbstract {
          c = is.read(buffer);
       }
    }
+
+   private enum SecurityManagerType {
+      JAAS, BASIC;
+
+      public static SecurityManagerType getType(String type) {
+         type = type.toLowerCase();
+         switch (type) {
+            case "jaas":
+               return JAAS;
+            case "basic":
+               return BASIC;
+            default:
+               return null;
+         }
+      }
+   }
 }
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
index 552f7ef..4f0da7c 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/Run.java
@@ -72,17 +72,15 @@ public class Run extends LockAbstract {
       super.execute(context);
 
       try {
+         BrokerDTO broker = getBrokerDTO();
+         ActiveMQSecurityManager securityManager = SecurityManagerFactory.create(broker.security);
          ManagementContextDTO managementDTO = getManagementDTO();
-         managementContext = ManagementFactory.create(managementDTO);
+         managementContext = ManagementFactory.create(managementDTO, securityManager);
 
          Artemis.printBanner();
 
-         BrokerDTO broker = getBrokerDTO();
-
          addShutdownHook(broker.server.getConfigurationFile().getParentFile());
 
-         ActiveMQSecurityManager security = SecurityManagerFactory.create(broker.security);
-
          ActivateCallback activateCallback = new ActivateCallback() {
             @Override
             public void preActivate() {
@@ -109,7 +107,7 @@ public class Run extends LockAbstract {
             }
          };
 
-         server = BrokerFactory.createServer(broker.server, security, activateCallback);
+         server = BrokerFactory.createServer(broker.server, securityManager, activateCallback);
 
          managementContext.start();
          server.createComponents();
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/jmx/ManagementFactory.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/jmx/ManagementFactory.java
index 7baa0f5..826dc8e 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/jmx/ManagementFactory.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/factory/jmx/ManagementFactory.java
@@ -27,6 +27,7 @@ import org.apache.activemq.artemis.dto.JMXConnectorDTO;
 import org.apache.activemq.artemis.dto.ManagementContextDTO;
 import org.apache.activemq.artemis.dto.MatchDTO;
 import org.apache.activemq.artemis.core.server.management.JMXAccessControlList;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
 import org.apache.activemq.artemis.utils.FactoryFinder;
 
 import java.io.IOException;
@@ -60,7 +61,7 @@ public class ManagementFactory {
       return createJmxAclConfiguration(new URI(configuration), artemisHome, artemisInstance, artemisURIInstance);
    }
 
-   public static ManagementContext create(ManagementContextDTO config) throws Exception {
+   public static ManagementContext create(ManagementContextDTO config, ActiveMQSecurityManager securityManager) throws Exception {
       ManagementContext context = new ManagementContext();
 
       if (config.getAuthorisation() != null) {
@@ -130,6 +131,7 @@ public class ManagementFactory {
             jmxConnectorConfiguration.setSecured(jmxConnector.isSecured());
          }
          context.setJmxConnectorConfiguration(jmxConnectorConfiguration);
+         context.setSecurityManager(securityManager);
       }
 
       return context;
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/basic-security-manager.txt b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/basic-security-manager.txt
new file mode 100644
index 0000000..d7dfbd9
--- /dev/null
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/basic-security-manager.txt
@@ -0,0 +1,5 @@
+   <security-manager class-name="org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager">
+      <property key="bootstrapUser" value="${user}"/>
+      <property key="bootstrapPassword" value="${password}"/>
+      <property key="bootstrapRole" value="${role}"/>
+   </security-manager>
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 f919428..69e2d95 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,7 +18,7 @@
 
 <broker xmlns="http://activemq.org/schema">
 
-   <jaas-security domain="activemq"/>
+${security-manager-settings}
 
    <!-- artemis.URI.instance is parsed from artemis.instance by the CLI startup.
         This is to avoid situations where you could have spaces or special characters on this URI -->
diff --git a/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/jaas-security-manager.txt b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/jaas-security-manager.txt
new file mode 100644
index 0000000..2a0260a
--- /dev/null
+++ b/artemis-cli/src/main/resources/org/apache/activemq/artemis/cli/commands/etc/jaas-security-manager.txt
@@ -0,0 +1 @@
+   <jaas-security domain="activemq"/>
diff --git a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java
index 5269e64..dab321a 100644
--- a/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java
+++ b/artemis-cli/src/test/java/org/apache/activemq/cli/test/ArtemisTest.java
@@ -105,6 +105,9 @@ import static org.junit.Assert.fail;
 public class ArtemisTest extends CliTestBase {
    private static final Logger log = Logger.getLogger(ArtemisTest.class);
 
+   // some tests will set this, as some security methods will need to know the server the CLI started
+   private ActiveMQServer server;
+
    @Before
    @Override
    public void setup() throws Exception {
@@ -340,13 +343,23 @@ public class ArtemisTest extends CliTestBase {
    }
 
    @Test
-   public void testUserCommand() throws Exception {
+   public void testUserCommandJAAS() throws Exception {
+      testUserCommand(false);
+   }
+
+   @Test
+   public void testUserCommandBasic() throws Exception {
+      testUserCommand(true);
+   }
+
+   private void testUserCommand(boolean basic) throws Exception {
       Run.setEmbedded(true);
       File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
       System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
-      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login");
+      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login", "--security-manager", basic ? "basic" : "jaas");
       System.setProperty("artemis.instance", instance1.getAbsolutePath());
-      Artemis.internalExecute("run");
+      Object runResult = Artemis.internalExecute("run");
+      server = ((Pair<ManagementContext, ActiveMQServer>)runResult).getB();
 
       try {
          File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
@@ -363,7 +376,7 @@ public class ArtemisTest extends CliTestBase {
 
          //default only one user admin with role amq
          assertTrue(result.contains("\"admin\"(amq)"));
-         checkRole("admin", roleFile, "amq");
+         checkRole("admin", roleFile, basic, "amq");
 
          //add a simple user
          AddUser addCmd = new AddUser();
@@ -383,8 +396,8 @@ public class ArtemisTest extends CliTestBase {
          assertTrue(result.contains("\"admin\"(amq)"));
          assertTrue(result.contains("\"guest\"(admin)"));
 
-         checkRole("guest", roleFile, "admin");
-         assertTrue(checkPassword("guest", "guest123", userFile));
+         checkRole("guest", roleFile, basic, "admin");
+         assertTrue(checkPassword("guest", "guest123", userFile, basic));
 
          //add a user with 2 roles
          addCmd = new AddUser();
@@ -405,8 +418,8 @@ public class ArtemisTest extends CliTestBase {
          assertTrue(result.contains("\"guest\"(admin)"));
          assertTrue(result.contains("\"scott\"(admin,operator)"));
 
-         checkRole("scott", roleFile, "admin", "operator");
-         assertTrue(checkPassword("scott", "tiger", userFile));
+         checkRole("scott", roleFile, basic, "admin", "operator");
+         assertTrue(checkPassword("scott", "tiger", userFile, basic));
 
          //add an existing user
          addCmd = new AddUser();
@@ -504,24 +517,34 @@ public class ArtemisTest extends CliTestBase {
    }
 
    @Test
-   public void testUserCommandViaManagementPlaintext() throws Exception {
-      internalTestUserCommandViaManagement(true);
+   public void testUserCommandViaManagementPlaintextJAAS() throws Exception {
+      internalTestUserCommandViaManagement(true, false);
+   }
+
+   @Test
+   public void testUserCommandViaManagementHashedJAAS() throws Exception {
+      internalTestUserCommandViaManagement(false, false);
    }
 
    @Test
-   public void testUserCommandViaManagementHashed() throws Exception {
-      internalTestUserCommandViaManagement(false);
+   public void testUserCommandViaManagementPlaintextBasic() throws Exception {
+      internalTestUserCommandViaManagement(true, true);
    }
 
-   private void internalTestUserCommandViaManagement(boolean plaintext) throws Exception {
+   @Test
+   public void testUserCommandViaManagementHashedBasic() throws Exception {
+      internalTestUserCommandViaManagement(false, true);
+   }
+
+   private void internalTestUserCommandViaManagement(boolean plaintext, boolean basic) throws Exception {
       Run.setEmbedded(true);
       File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
       System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
-      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor");
+      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor", "--security-manager", basic ? "basic" : "jaas");
       System.setProperty("artemis.instance", instance1.getAbsolutePath());
       Object result = Artemis.internalExecute("run");
-      ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
-      ActiveMQServerControl activeMQServerControl = activeMQServer.getActiveMQServerControl();
+      server = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
+      ActiveMQServerControl activeMQServerControl = server.getActiveMQServerControl();
 
       File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
       File roleFile = new File(instance1.getAbsolutePath() + "/etc/artemis-roles.properties");
@@ -529,7 +552,7 @@ public class ArtemisTest extends CliTestBase {
       //default only one user admin with role amq
       String jsonResult = activeMQServerControl.listUser("");
       contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
-      checkRole("admin", roleFile, "amq");
+      checkRole("admin", roleFile, basic, "amq");
 
       //add a simple user
       activeMQServerControl.addUser("guest", "guest123", "admin", plaintext);
@@ -537,9 +560,9 @@ public class ArtemisTest extends CliTestBase {
       //verify add
       jsonResult = activeMQServerControl.listUser("");
       contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin");
-      checkRole("guest", roleFile, "admin");
-      assertTrue(checkPassword("guest", "guest123", userFile));
-      assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("guest", userFile)));
+      checkRole("guest", roleFile, basic, "admin");
+      assertTrue(checkPassword("guest", "guest123", userFile, basic));
+      assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("guest", userFile, basic)));
 
       //add a user with 2 roles
       activeMQServerControl.addUser("scott", "tiger", "admin,operator", plaintext);
@@ -548,9 +571,9 @@ public class ArtemisTest extends CliTestBase {
       jsonResult = activeMQServerControl.listUser("");
       contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin");
       contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator");
-      checkRole("scott", roleFile, "admin", "operator");
-      assertTrue(checkPassword("scott", "tiger", userFile));
-      assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("scott", userFile)));
+      checkRole("scott", roleFile, basic, "admin", "operator");
+      assertTrue(checkPassword("scott", "tiger", userFile, basic));
+      assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("scott", userFile, basic)));
 
       try {
          activeMQServerControl.addUser("scott", "password", "visitor", plaintext);
@@ -615,11 +638,20 @@ public class ArtemisTest extends CliTestBase {
    }
 
    @Test
-   public void testProperReloadWhenAddingUserViaManagement() throws Exception {
+   public void testProperReloadWhenAddingUserViaManagementJAAS() throws Exception {
+      testProperReloadWhenAddingUserViaManagement(false);
+   }
+
+   @Test
+   public void testProperReloadWhenAddingUserViaManagementBasic() throws Exception {
+      testProperReloadWhenAddingUserViaManagement(true);
+   }
+
+   private void testProperReloadWhenAddingUserViaManagement(boolean basic) throws Exception {
       Run.setEmbedded(true);
       File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
       System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
-      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor", "--require-login");
+      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor", "--require-login", "--security-manager", basic ? "basic" : "jaas");
       System.setProperty("artemis.instance", instance1.getAbsolutePath());
       Object result = Artemis.internalExecute("run");
       ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
@@ -702,13 +734,23 @@ public class ArtemisTest extends CliTestBase {
    }
 
    @Test
-   public void testUserCommandReset() throws Exception {
+   public void testUserCommandResetJAAS() throws Exception {
+      testUserCommandReset(false);
+   }
+
+   @Test
+   public void testUserCommandResetBasic() throws Exception {
+      testUserCommandReset(true);
+   }
+
+   private void testUserCommandReset(boolean basic) throws Exception {
       Run.setEmbedded(true);
       File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
       System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
-      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login");
+      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login", "--security-manager", basic ? "basic" : "jaas");
       System.setProperty("artemis.instance", instance1.getAbsolutePath());
-      Artemis.internalExecute("run");
+      Object runResult = Artemis.internalExecute("run");
+      server = ((Pair<ManagementContext, ActiveMQServer>)runResult).getB();
 
       try {
          File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
@@ -754,7 +796,7 @@ public class ArtemisTest extends CliTestBase {
          addCmd.setUserCommandPassword("password1");
          addCmd.setRole("admin,manager");
          addCmd.execute(new TestActionContext());
-         assertTrue(checkPassword("user1", "password1", userFile));
+         assertTrue(checkPassword("user1", "password1", userFile, basic));
 
          addCmd.setUserCommandUser("user2");
          addCmd.setUserCommandPassword("password2");
@@ -787,7 +829,7 @@ public class ArtemisTest extends CliTestBase {
                        .matcher(result)
                        .find());
 
-         checkRole("user1", roleFile, "admin", "manager");
+         checkRole("user1", roleFile, basic, "admin", "manager");
 
          //reset password
          context = new TestActionContext();
@@ -798,16 +840,16 @@ public class ArtemisTest extends CliTestBase {
          resetCommand.setPassword("admin");
          resetCommand.execute(context);
 
-         checkRole("user1", roleFile, "admin", "manager");
-         assertFalse(checkPassword("user1", "password1", userFile));
-         assertTrue(checkPassword("user1", "newpassword1", userFile));
+         checkRole("user1", roleFile, basic, "admin", "manager");
+         assertFalse(checkPassword("user1", "password1", userFile, basic));
+         assertTrue(checkPassword("user1", "newpassword1", userFile, basic));
 
          //reset role
          resetCommand.setUserCommandUser("user2");
          resetCommand.setRole("manager,master,operator");
          resetCommand.execute(new TestActionContext());
 
-         checkRole("user2", roleFile, "manager", "master", "operator");
+         checkRole("user2", roleFile, basic, "manager", "master", "operator");
 
          //reset both
          resetCommand.setUserCommandUser("user3");
@@ -815,19 +857,28 @@ public class ArtemisTest extends CliTestBase {
          resetCommand.setRole("admin,system");
          resetCommand.execute(new ActionContext());
 
-         checkRole("user3", roleFile, "admin", "system");
-         assertTrue(checkPassword("user3", "newpassword3", userFile));
+         checkRole("user3", roleFile, basic, "admin", "system");
+         assertTrue(checkPassword("user3", "newpassword3", userFile, basic));
       } finally {
          stopServer();
       }
    }
 
    @Test
-   public void testConcurrentUserAdministration() throws Exception {
+   public void testConcurrentUserAdministrationJAAS() throws Exception {
+      testConcurrentUserAdministration(false);
+   }
+
+   @Test
+   public void testConcurrentUserAdministrationBasic() throws Exception {
+      testConcurrentUserAdministration(true);
+   }
+
+   private void testConcurrentUserAdministration(boolean basic) throws Exception {
       Run.setEmbedded(true);
       File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
       System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
-      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login");
+      Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login", "--security-manager", basic ? "basic" : "jaas");
       System.setProperty("artemis.instance", instance1.getAbsolutePath());
       Artemis.internalExecute("run");
 
@@ -1711,28 +1762,52 @@ public class ArtemisTest extends CliTestBase {
    }
 
    private void checkRole(String user, File roleFile, String... roles) throws Exception {
-      Configurations configs = new Configurations();
-      FileBasedConfigurationBuilder<PropertiesConfiguration> roleBuilder = configs.propertiesBuilder(roleFile);
-      PropertiesConfiguration roleConfig = roleBuilder.getConfiguration();
+      checkRole(user, roleFile, false, roles);
+   }
+
+   private void checkRole(String user, File roleFile, boolean basicSecurityManager, String... roles) throws Exception {
+      if (basicSecurityManager) {
+         for (String r : roles) {
+            assertTrue(server.getStorageManager().getPersistedRoles().get(user).getRoles().contains(r));
+         }
+      } else {
+         Configurations configs = new Configurations();
+         FileBasedConfigurationBuilder<PropertiesConfiguration> roleBuilder = configs.propertiesBuilder(roleFile);
+         PropertiesConfiguration roleConfig = roleBuilder.getConfiguration();
 
-      for (String r : roles) {
-         String storedUsers = (String) roleConfig.getProperty(r);
+         for (String r : roles) {
+            String storedUsers = (String) roleConfig.getProperty(r);
 
-         log.debug("users in role: " + r + " ; " + storedUsers);
-         List<String> userList = StringUtil.splitStringList(storedUsers, ",");
-         assertTrue(userList.contains(user));
+            log.debug("users in role: " + r + " ; " + storedUsers);
+            List<String> userList = StringUtil.splitStringList(storedUsers, ",");
+            assertTrue(userList.contains(user));
+         }
       }
    }
 
    private String getStoredPassword(String user, File userFile) throws Exception {
-      Configurations configs = new Configurations();
-      FileBasedConfigurationBuilder<PropertiesConfiguration> userBuilder = configs.propertiesBuilder(userFile);
-      PropertiesConfiguration userConfig = userBuilder.getConfiguration();
-      return (String) userConfig.getProperty(user);
+      return getStoredPassword(user, userFile, false);
+   }
+
+   private String getStoredPassword(String user, File userFile, boolean basicSecurityManager) throws Exception {
+      String result;
+      if (basicSecurityManager) {
+         result = server.getStorageManager().getPersistedUsers().get(user).getPassword();
+      } else {
+         Configurations configs = new Configurations();
+         FileBasedConfigurationBuilder<PropertiesConfiguration> userBuilder = configs.propertiesBuilder(userFile);
+         PropertiesConfiguration userConfig = userBuilder.getConfiguration();
+         result = (String) userConfig.getProperty(user);
+      }
+      return result;
    }
 
    private boolean checkPassword(String user, String password, File userFile) throws Exception {
-      String storedPassword = getStoredPassword(user, userFile);
+      return checkPassword(user, password, userFile,false);
+   }
+
+   private boolean checkPassword(String user, String password, File userFile, boolean basicSecurityManager) throws Exception {
+      String storedPassword = getStoredPassword(user, userFile, basicSecurityManager);
       HashProcessor processor = PasswordMaskingUtil.getHashProcessor(storedPassword);
       return processor.compare(password.toCharArray(), storedPassword);
    }
diff --git a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java
index e9df0b2..4c64a45 100644
--- a/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java
+++ b/artemis-commons/src/main/java/org/apache/activemq/artemis/utils/PasswordMaskingUtil.java
@@ -37,6 +37,18 @@ public final class PasswordMaskingUtil {
 
    /**
     * This method deals with password masking and returns the password in its plain text form.
+    * @param password : the original value of password string; interpreted as masked if wrapped in ENC()
+    *                 or as plain text otherwise.
+    * @param codecClass : the codec used to decode the password. Only when the password is interpreted
+    *              as masked will this codec be used. Ignored otherwise.
+    * @return
+    */
+   public static String resolveMask(String password, String codecClass) throws Exception {
+      return resolveMask(null, password, codecClass);
+   }
+
+   /**
+    * This method deals with password masking and returns the password in its plain text form.
     * @param maskPassword : explicit mask flag. If it's true, the password is interpreted as
     *                     masked. If it is false, the password is interpreted as plain text.
     *                     if it is null, the password will be interpreted as masked if the
@@ -109,7 +121,7 @@ public final class PasswordMaskingUtil {
    }
 
    //stored password takes 2 forms, ENC() or plain text
-   public static HashProcessor getHashProcessor(String storedPassword) throws Exception {
+   public static HashProcessor getHashProcessor(String storedPassword) {
 
       if (!isEncoded(storedPassword)) {
          return LazyPlainTextProcessorHolder.INSTANCE;
diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java
index ae8e266..5a22730 100644
--- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java
+++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java
@@ -1830,45 +1830,45 @@ public interface ActiveMQServerControl {
     * @param roles
     * @throws Exception
     */
-   @Operation(desc = "add a user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
+   @Operation(desc = "add a user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
    void addUser(@Parameter(name = "username", desc = "Name of the user") String username,
                 @Parameter(name = "password", desc = "User's password") String password,
                 @Parameter(name = "roles", desc = "User's role (comma separated)") String roles,
                 @Parameter(name = "plaintext", desc = "whether or not to store the password in plaintext or hash it") boolean plaintext) throws Exception;
 
    /**
-    * List the information about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule).
+    * List the information about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager).
     *
     * @param username
     * @return JSON array of user and role information
     * @throws Exception
     */
-   @Operation(desc = "list info about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
+   @Operation(desc = "list info about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
    String listUser(@Parameter(name = "username", desc = "Name of the user; leave null to list all known users") String username) throws Exception;
 
    /**
-    * Remove a user (only applicable when using the JAAS PropertiesLoginModule).
+    * Remove a user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager).
     *
     * @param username
     * @throws Exception
     */
-   @Operation(desc = "remove a user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
+   @Operation(desc = "remove a user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
    void removeUser(@Parameter(name = "username", desc = "Name of the user") String username) throws Exception;
 
    /**
-    * Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule).
+    * Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager).
     *
     * @param username
     * @param password
     * @param roles
     * @throws Exception
     */
-   @Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
+   @Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
    void resetUser(@Parameter(name = "username", desc = "Name of the user") String username,
                   @Parameter(name = "password", desc = "User's password") String password,
                   @Parameter(name = "roles", desc = "User's role (comma separated)") String roles) throws Exception;
    /**
-    * Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule).
+    * Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager).
     *
     * @param username
     * @param password
@@ -1877,7 +1877,7 @@ public interface ActiveMQServerControl {
     * @throws Exception
     */
 
-   @Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
+   @Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
    void resetUser(@Parameter(name = "username", desc = "Name of the user") String username,
                   @Parameter(name = "password", desc = "User's password") String password,
                   @Parameter(name = "roles", desc = "User's role (comma separated)") String roles,
diff --git a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/JMXConnectorDTO.java b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/JMXConnectorDTO.java
index 965de93..4805551 100644
--- a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/JMXConnectorDTO.java
+++ b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/JMXConnectorDTO.java
@@ -123,6 +123,6 @@ public class JMXConnectorDTO {
    }
 
    private String getPassword(String password) throws Exception {
-      return PasswordMaskingUtil.resolveMask(null, password, this.passwordCodec);
+      return PasswordMaskingUtil.resolveMask(password, this.passwordCodec);
    }
 }
diff --git a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java
index 4cc627a..7492085 100644
--- a/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java
+++ b/artemis-dto/src/main/java/org/apache/activemq/artemis/dto/WebServerDTO.java
@@ -83,7 +83,7 @@ public class WebServerDTO extends ComponentDTO {
    }
 
    private String getPassword(String password) throws Exception {
-      return PasswordMaskingUtil.resolveMask(null, password, this.passwordCodec);
+      return PasswordMaskingUtil.resolveMask(password, this.passwordCodec);
    }
 
    public void setKeyStorePassword(String keyStorePassword) {
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java
index 92608cc..758a8b7 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java
@@ -83,7 +83,7 @@ import org.apache.activemq.artemis.core.messagecounter.impl.MessageCounterManage
 import org.apache.activemq.artemis.core.persistence.StorageManager;
 import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
 import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
-import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
+import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
 import org.apache.activemq.artemis.core.postoffice.Binding;
 import org.apache.activemq.artemis.core.postoffice.Bindings;
 import org.apache.activemq.artemis.core.postoffice.DuplicateIDCache;
@@ -96,9 +96,9 @@ import org.apache.activemq.artemis.core.security.impl.SecurityStoreImpl;
 import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
 import org.apache.activemq.artemis.core.server.ActiveMQServer;
 import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+import org.apache.activemq.artemis.core.server.ComponentConfigurationRoutingType;
 import org.apache.activemq.artemis.core.server.ConnectorServiceFactory;
 import org.apache.activemq.artemis.core.server.Consumer;
-import org.apache.activemq.artemis.core.server.ComponentConfigurationRoutingType;
 import org.apache.activemq.artemis.core.server.Divert;
 import org.apache.activemq.artemis.core.server.JournalType;
 import org.apache.activemq.artemis.core.server.Queue;
@@ -127,6 +127,7 @@ import org.apache.activemq.artemis.core.transaction.impl.CoreTransactionDetail;
 import org.apache.activemq.artemis.core.transaction.impl.XidImpl;
 import org.apache.activemq.artemis.logs.AuditLogger;
 import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
 import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModuleConfigurator;
 import org.apache.activemq.artemis.utils.JsonLoader;
 import org.apache.activemq.artemis.utils.ListUtil;
@@ -2784,9 +2785,9 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
 
          server.getSecurityRepository().addMatch(addressMatch, roles);
 
-         PersistedRoles persistedRoles = new PersistedRoles(addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles, createAddressRoles, deleteAddressRoles);
+         PersistedSecuritySetting persistedRoles = new PersistedSecuritySetting(addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles, createAddressRoles, deleteAddressRoles);
 
-         storageManager.storeSecurityRoles(persistedRoles);
+         storageManager.storeSecuritySetting(persistedRoles);
       } finally {
          blockOnIO();
       }
@@ -2802,7 +2803,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
       clearIO();
       try {
          server.getSecurityRepository().removeMatch(addressMatch);
-         storageManager.deleteSecurityRoles(new SimpleString(addressMatch));
+         storageManager.deleteSecuritySetting(new SimpleString(addressMatch));
       } finally {
          blockOnIO();
       }
@@ -4189,15 +4190,22 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
 
    @Override
    public void addUser(String username, String password, String roles, boolean plaintext) throws Exception {
-      synchronized (userLock) {
-         if (AuditLogger.isEnabled()) {
-            AuditLogger.addUser(this.server, username, "****", roles, plaintext);
+      if (AuditLogger.isEnabled()) {
+         AuditLogger.addUser(this.server, username, "****", roles, plaintext);
+      }
+
+      String passwordToUse = plaintext ? password : PasswordMaskingUtil.getHashProcessor().hash(password);
+
+      if (server.getSecurityManager() instanceof ActiveMQBasicSecurityManager) {
+         ((ActiveMQBasicSecurityManager) server.getSecurityManager()).addNewUser(username, passwordToUse, roles.split(","));
+      } else {
+         synchronized (userLock) {
+            tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
+               PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
+               config.addNewUser(username, passwordToUse, roles.split(","));
+               config.save();
+            });
          }
-         tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
-            PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
-            config.addNewUser(username, plaintext ? password : PasswordMaskingUtil.getHashProcessor().hash(password), roles.split(","));
-            config.save();
-         });
       }
    }
 
@@ -4207,58 +4215,72 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
 
    @Override
    public String listUser(String username) throws Exception {
-      synchronized (userLock) {
-         if (AuditLogger.isEnabled()) {
-            AuditLogger.listUser(this.server, username);
+      if (AuditLogger.isEnabled()) {
+         AuditLogger.listUser(this.server, username);
+      }
+      if (server.getSecurityManager() instanceof ActiveMQBasicSecurityManager) {
+         return buildJsonUserList(((ActiveMQBasicSecurityManager) server.getSecurityManager()).listUser(username));
+      } else {
+         synchronized (userLock) {
+            return (String) tcclCall(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
+               return buildJsonUserList(getPropertiesLoginModuleConfigurator().listUser(username));
+            });
          }
+      }
+   }
 
-         return (String) tcclCall(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
-            PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
-            Map<String, Set<String>> info = config.listUser(username);
-            JsonArrayBuilder users = JsonLoader.createArrayBuilder();
-            for (Entry<String, Set<String>> entry : info.entrySet()) {
-               JsonObjectBuilder user = JsonLoader.createObjectBuilder();
-               user.add("username", entry.getKey());
-               JsonArrayBuilder roles = JsonLoader.createArrayBuilder();
-               for (String role : entry.getValue()) {
-                  roles.add(role);
-               }
-               user.add("roles", roles);
-               users.add(user);
-            }
-            return users.build().toString();
-         });
+   private String buildJsonUserList(Map<String, Set<String>> info) {
+      JsonArrayBuilder users = JsonLoader.createArrayBuilder();
+      for (Entry<String, Set<String>> entry : info.entrySet()) {
+         JsonObjectBuilder user = JsonLoader.createObjectBuilder();
+         user.add("username", entry.getKey());
+         JsonArrayBuilder roles = JsonLoader.createArrayBuilder();
+         for (String role : entry.getValue()) {
+            roles.add(role);
+         }
+         user.add("roles", roles);
+         users.add(user);
       }
+      return users.build().toString();
    }
 
    @Override
    public void removeUser(String username) throws Exception {
-      synchronized (userLock) {
-         if (AuditLogger.isEnabled()) {
-            AuditLogger.removeUser(this.server, username);
+      if (AuditLogger.isEnabled()) {
+         AuditLogger.removeUser(this.server, username);
+      }
+      if (server.getSecurityManager() instanceof ActiveMQBasicSecurityManager) {
+         ((ActiveMQBasicSecurityManager) server.getSecurityManager()).removeUser(username);
+      } else {
+         synchronized (userLock) {
+            tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
+               PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
+               config.removeUser(username);
+               config.save();
+            });
          }
-         tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
-            PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
-            config.removeUser(username);
-            config.save();
-         });
       }
    }
 
    @Override
    public void resetUser(String username, String password, String roles, boolean plaintext) throws Exception {
-      synchronized (userLock) {
-         if (AuditLogger.isEnabled()) {
-            AuditLogger.resetUser(this.server, username, "****", roles, plaintext);
+      if (AuditLogger.isEnabled()) {
+         AuditLogger.resetUser(this.server, username, "****", roles, plaintext);
+      }
+
+      String passwordToUse = password == null ? password : plaintext ? password : PasswordMaskingUtil.getHashProcessor().hash(password);
+
+      if (server.getSecurityManager() instanceof ActiveMQBasicSecurityManager) {
+         ((ActiveMQBasicSecurityManager) server.getSecurityManager()).updateUser(username, passwordToUse, roles == null ? null : roles.split(","));
+      } else {
+         synchronized (userLock) {
+            tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
+               PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
+               // don't hash a null password even if plaintext = false
+               config.updateUser(username, passwordToUse, roles == null ? null : roles.split(","));
+               config.save();
+            });
          }
-         tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
-            PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
-            // don't hash a null password even if plaintext = false
-            config.updateUser(username, password == null ? password : plaintext ? password : PasswordMaskingUtil
-               .getHashProcessor()
-               .hash(password), roles == null ? null : roles.split(","));
-            config.save();
-         });
       }
    }
 
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/StorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/StorageManager.java
index 5ee4478..a35d2a9 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/StorageManager.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/StorageManager.java
@@ -41,7 +41,9 @@ import org.apache.activemq.artemis.core.paging.PagingStore;
 import org.apache.activemq.artemis.core.paging.cursor.PagePosition;
 import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
 import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
-import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
+import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
+import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
+import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
 import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
 import org.apache.activemq.artemis.core.postoffice.Binding;
 import org.apache.activemq.artemis.core.postoffice.PostOffice;
@@ -354,17 +356,30 @@ public interface StorageManager extends IDGenerator, ActiveMQComponent {
 
    List<PersistedAddressSetting> recoverAddressSettings() throws Exception;
 
-   void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception;
+   void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception;
 
-   void deleteSecurityRoles(SimpleString addressMatch) throws Exception;
+   void deleteSecuritySetting(SimpleString addressMatch) throws Exception;
 
-   List<PersistedRoles> recoverPersistedRoles() throws Exception;
+   List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception;
 
    void storeDivertConfiguration(PersistedDivertConfiguration persistedDivertConfiguration) throws Exception;
 
    void deleteDivertConfiguration(String divertName) throws Exception;
 
    List<PersistedDivertConfiguration> recoverDivertConfigurations();
+
+   void storeUser(PersistedUser persistedUser) throws Exception;
+
+   void deleteUser(String username) throws Exception;
+
+   Map<String, PersistedUser> getPersistedUsers();
+
+   void storeRole(PersistedRole persistedRole) throws Exception;
+
+   void deleteRole(String role) throws Exception;
+
+   Map<String, PersistedRole> getPersistedRoles();
+
    /**
     * @return The ID with the stored counter
     */
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedRole.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedRole.java
new file mode 100644
index 0000000..1af8864
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedRole.java
@@ -0,0 +1,108 @@
+/*
+ * 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.persistence.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
+import org.apache.activemq.artemis.core.journal.EncodingSupport;
+import org.apache.activemq.artemis.utils.BufferHelper;
+import org.apache.activemq.artemis.utils.DataConstants;
+
+public class PersistedRole implements EncodingSupport {
+
+   private long storeId;
+
+   private String username;
+
+   private List<String> roles;
+
+   public PersistedRole() {
+   }
+
+   public PersistedRole(String username, List<String> roles) {
+      this.username = username;
+      this.roles = roles;
+   }
+
+   public void setStoreId(long id) {
+      this.storeId = id;
+   }
+
+   public long getStoreId() {
+      return storeId;
+   }
+
+   public String getUsername() {
+      return username;
+   }
+
+   public List<String> getRoles() {
+      return roles;
+   }
+
+   @Override
+   public int getEncodeSize() {
+      int size = 0;
+      size += BufferHelper.sizeOfString(username);
+      size += DataConstants.SIZE_INT;
+      for (String role : roles) {
+         size += BufferHelper.sizeOfString(role);
+      }
+      return size;
+   }
+
+   @Override
+   public void encode(ActiveMQBuffer buffer) {
+      buffer.writeString(username);
+      buffer.writeInt(roles.size());
+      for (String user : roles) {
+         buffer.writeString(user);
+      }
+   }
+
+   @Override
+   public void decode(ActiveMQBuffer buffer) {
+      username = buffer.readString();
+      roles = new ArrayList<>();
+      int size = buffer.readInt();
+      for (int i = 0; i < size; i++) {
+         roles.add(buffer.readString());
+      }
+   }
+
+   /* (non-Javadoc)
+    * @see java.lang.Object#toString()
+    */
+   @Override
+   public String toString() {
+      StringBuilder result = new StringBuilder();
+      result.append("PersistedRole [storeId=").append(storeId);
+      result.append(", username=").append(username);
+      result.append(", roles [");
+      for (int i = 0; i < roles.size(); i++) {
+         result.append(roles.get(i));
+         if (i < roles.size() - 1) {
+            result.append(", ");
+         }
+      }
+      result.append("]]");
+
+      return result.toString();
+   }
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedRoles.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedSecuritySetting.java
similarity index 92%
rename from artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedRoles.java
rename to artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedSecuritySetting.java
index 86fdc14..f175be4 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedRoles.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedSecuritySetting.java
@@ -20,7 +20,7 @@ import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
 import org.apache.activemq.artemis.api.core.SimpleString;
 import org.apache.activemq.artemis.core.journal.EncodingSupport;
 
-public class PersistedRoles implements EncodingSupport {
+public class PersistedSecuritySetting implements EncodingSupport {
 
    // Constants -----------------------------------------------------
 
@@ -54,7 +54,7 @@ public class PersistedRoles implements EncodingSupport {
 
    // Constructors --------------------------------------------------
 
-   public PersistedRoles() {
+   public PersistedSecuritySetting() {
    }
 
    /**
@@ -70,17 +70,17 @@ public class PersistedRoles implements EncodingSupport {
     * @param createAddressRoles
     * @param deleteAddressRoles
     */
-   public PersistedRoles(final String addressMatch,
-                         final String sendRoles,
-                         final String consumeRoles,
-                         final String createDurableQueueRoles,
-                         final String deleteDurableQueueRoles,
-                         final String createNonDurableQueueRoles,
-                         final String deleteNonDurableQueueRoles,
-                         final String manageRoles,
-                         final String browseRoles,
-                         final String createAddressRoles,
-                         final String deleteAddressRoles) {
+   public PersistedSecuritySetting(final String addressMatch,
+                                   final String sendRoles,
+                                   final String consumeRoles,
+                                   final String createDurableQueueRoles,
+                                   final String deleteDurableQueueRoles,
+                                   final String createNonDurableQueueRoles,
+                                   final String deleteNonDurableQueueRoles,
+                                   final String manageRoles,
+                                   final String browseRoles,
+                                   final String createAddressRoles,
+                                   final String deleteAddressRoles) {
       super();
       this.addressMatch = SimpleString.toSimpleString(addressMatch);
       this.sendRoles = SimpleString.toSimpleString(sendRoles);
@@ -259,7 +259,7 @@ public class PersistedRoles implements EncodingSupport {
          return false;
       if (getClass() != obj.getClass())
          return false;
-      PersistedRoles other = (PersistedRoles) obj;
+      PersistedSecuritySetting other = (PersistedSecuritySetting) obj;
       if (addressMatch == null) {
          if (other.addressMatch != null)
             return false;
@@ -325,7 +325,7 @@ public class PersistedRoles implements EncodingSupport {
     */
    @Override
    public String toString() {
-      return "PersistedRoles [storeId=" + storeId +
+      return "PersistedSecuritySetting [storeId=" + storeId +
          ", addressMatch=" +
          addressMatch +
          ", sendRoles=" +
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedUser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedUser.java
new file mode 100644
index 0000000..aef5dec
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedUser.java
@@ -0,0 +1,86 @@
+/*
+ * 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.persistence.config;
+
+import org.apache.activemq.artemis.api.core.ActiveMQBuffer;
+import org.apache.activemq.artemis.core.journal.EncodingSupport;
+import org.apache.activemq.artemis.utils.BufferHelper;
+
+public class PersistedUser implements EncodingSupport {
+
+   private long storeId;
+
+   private String username;
+
+   private String password;
+
+   public PersistedUser() {
+   }
+
+   public PersistedUser(String username, String password) {
+      this.username = username;
+      this.password = password;
+   }
+
+   public void setStoreId(long id) {
+      this.storeId = id;
+   }
+
+   public long getStoreId() {
+      return storeId;
+   }
+
+   public String getUsername() {
+      return username;
+   }
+
+   public String getPassword() {
+      return password;
+   }
+
+   @Override
+   public int getEncodeSize() {
+      int size = 0;
+      size += BufferHelper.sizeOfString(username);
+      size += BufferHelper.sizeOfString(password);
+      return size;
+   }
+
+   @Override
+   public void encode(ActiveMQBuffer buffer) {
+      buffer.writeString(username);
+      buffer.writeString(password);
+   }
+
+   @Override
+   public void decode(ActiveMQBuffer buffer) {
+      username = buffer.readString();
+      password = buffer.readString();
+   }
+
+   /* (non-Javadoc)
+    * @see java.lang.Object#toString()
+    */
+   @Override
+   public String toString() {
+      return "PersistedUser [storeId=" + storeId +
+         ", username=" +
+         username +
+         ", password=****" +
+         "]";
+   }
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java
index 1cb39ab..daa7957 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/AbstractJournalStorageManager.java
@@ -75,7 +75,9 @@ import org.apache.activemq.artemis.core.persistence.AddressQueueStatus;
 import org.apache.activemq.artemis.core.persistence.StorageManager;
 import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
 import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
-import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
+import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
+import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
+import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
 import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
 import org.apache.activemq.artemis.core.persistence.impl.journal.codec.AddressStatusEncoding;
 import org.apache.activemq.artemis.core.persistence.impl.journal.codec.CursorAckRecordEncoding;
@@ -195,12 +197,16 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
    protected final Configuration config;
 
    // Persisted core configuration
-   protected final Map<SimpleString, PersistedRoles> mapPersistedRoles = new ConcurrentHashMap<>();
+   protected final Map<SimpleString, PersistedSecuritySetting> mapPersistedSecuritySettings = new ConcurrentHashMap<>();
 
    protected final Map<SimpleString, PersistedAddressSetting> mapPersistedAddressSettings = new ConcurrentHashMap<>();
 
    protected final Map<String, PersistedDivertConfiguration> mapPersistedDivertConfigurations = new ConcurrentHashMap<>();
 
+   protected final Map<String, PersistedUser> mapPersistedUsers = new ConcurrentHashMap<>();
+
+   protected final Map<String, PersistedRole> mapPersistedRoles = new ConcurrentHashMap<>();
+
    protected final ConcurrentLongHashMap<LargeServerMessage> largeMessagesToDelete = new ConcurrentLongHashMap<>();
 
    public AbstractJournalStorageManager(final Configuration config,
@@ -767,20 +773,20 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
    }
 
    @Override
-   public List<PersistedRoles> recoverPersistedRoles() throws Exception {
-      return new ArrayList<>(mapPersistedRoles.values());
+   public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
+      return new ArrayList<>(mapPersistedSecuritySettings.values());
    }
 
    @Override
-   public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
+   public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
 
-      deleteSecurityRoles(persistedRoles.getAddressMatch());
+      deleteSecuritySetting(persistedRoles.getAddressMatch());
       readLock();
       try {
          final long id = idGenerator.generateID();
          persistedRoles.setStoreId(id);
-         bindingsJournal.appendAddRecord(id, JournalRecordIds.SECURITY_RECORD, persistedRoles, true);
-         mapPersistedRoles.put(persistedRoles.getAddressMatch(), persistedRoles);
+         bindingsJournal.appendAddRecord(id, JournalRecordIds.SECURITY_SETTING_RECORD, persistedRoles, true);
+         mapPersistedSecuritySettings.put(persistedRoles.getAddressMatch(), persistedRoles);
       } finally {
          readUnLock();
       }
@@ -819,6 +825,70 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
    }
 
    @Override
+   public void storeUser(PersistedUser persistedUser) throws Exception {
+      deleteUser(persistedUser.getUsername());
+      readLock();
+      try {
+         final long id = idGenerator.generateID();
+         persistedUser.setStoreId(id);
+         bindingsJournal.appendAddRecord(id, JournalRecordIds.USER_RECORD, persistedUser, true);
+         mapPersistedUsers.put(persistedUser.getUsername(), persistedUser);
+      } finally {
+         readUnLock();
+      }
+   }
+
+   @Override
+   public void deleteUser(String username) throws Exception {
+      PersistedUser oldUser = mapPersistedUsers.remove(username);
+      if (oldUser != null) {
+         readLock();
+         try {
+            bindingsJournal.appendDeleteRecord(oldUser.getStoreId(), false);
+         } finally {
+            readUnLock();
+         }
+      }
+   }
+
+   @Override
+   public Map<String, PersistedUser> getPersistedUsers() {
+      return new HashMap<>(mapPersistedUsers);
+   }
+
+   @Override
+   public void storeRole(PersistedRole persistedRole) throws Exception {
+      deleteRole(persistedRole.getUsername());
+      readLock();
+      try {
+         final long id = idGenerator.generateID();
+         persistedRole.setStoreId(id);
+         bindingsJournal.appendAddRecord(id, JournalRecordIds.ROLE_RECORD, persistedRole, true);
+         mapPersistedRoles.put(persistedRole.getUsername(), persistedRole);
+      } finally {
+         readUnLock();
+      }
+   }
+
+   @Override
+   public void deleteRole(String username) throws Exception {
+      PersistedRole oldRole = mapPersistedRoles.remove(username);
+      if (oldRole != null) {
+         readLock();
+         try {
+            bindingsJournal.appendDeleteRecord(oldRole.getStoreId(), false);
+         } finally {
+            readUnLock();
+         }
+      }
+   }
+
+   @Override
+   public Map<String, PersistedRole> getPersistedRoles() {
+      return new HashMap<>(mapPersistedRoles);
+   }
+
+   @Override
    public void storeID(final long journalID, final long id) throws Exception {
       readLock();
       try {
@@ -852,8 +922,8 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
    }
 
    @Override
-   public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
-      PersistedRoles oldRoles = mapPersistedRoles.remove(addressMatch);
+   public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
+      PersistedSecuritySetting oldRoles = mapPersistedSecuritySettings.remove(addressMatch);
       if (oldRoles != null) {
          readLock();
          try {
@@ -1560,9 +1630,9 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
             } else if (rec == JournalRecordIds.ADDRESS_SETTING_RECORD) {
                PersistedAddressSetting setting = newAddressEncoding(id, buffer);
                mapPersistedAddressSettings.put(setting.getAddressMatch(), setting);
-            } else if (rec == JournalRecordIds.SECURITY_RECORD) {
-               PersistedRoles roles = newSecurityRecord(id, buffer);
-               mapPersistedRoles.put(roles.getAddressMatch(), roles);
+            } else if (rec == JournalRecordIds.SECURITY_SETTING_RECORD) {
+               PersistedSecuritySetting roles = newSecurityRecord(id, buffer);
+               mapPersistedSecuritySettings.put(roles.getAddressMatch(), roles);
             } else if (rec == JournalRecordIds.QUEUE_STATUS_RECORD) {
                QueueStatusEncoding statusEncoding = newQueueStatusEncoding(id, buffer);
                PersistentQueueBindingEncoding queueBindingEncoding = mapBindings.get(statusEncoding.queueID);
@@ -1586,6 +1656,12 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
             } else if (rec == JournalRecordIds.DIVERT_RECORD) {
                PersistedDivertConfiguration divertConfiguration = newDivertEncoding(id, buffer);
                mapPersistedDivertConfigurations.put(divertConfiguration.getName(), divertConfiguration);
+            } else if (rec == JournalRecordIds.USER_RECORD) {
+               PersistedUser user = newUserEncoding(id, buffer);
+               mapPersistedUsers.put(user.getUsername(), user);
+            } else if (rec == JournalRecordIds.ROLE_RECORD) {
+               PersistedRole role = newRoleEncoding(id, buffer);
+               mapPersistedRoles.put(role.getUsername(), role);
             } else {
                // unlikely to happen
                ActiveMQServerLogger.LOGGER.invalidRecordType(rec, new Exception("invalid record type " + rec));
@@ -2042,8 +2118,8 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
     * @param buffer
     * @return
     */
-   protected static PersistedRoles newSecurityRecord(long id, ActiveMQBuffer buffer) {
-      PersistedRoles roles = new PersistedRoles();
+   protected static PersistedSecuritySetting newSecurityRecord(long id, ActiveMQBuffer buffer) {
+      PersistedSecuritySetting roles = new PersistedSecuritySetting();
       roles.decode(buffer);
       roles.setStoreId(id);
       return roles;
@@ -2074,6 +2150,20 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
       persistedDivertConfiguration.setStoreId(id);
       return persistedDivertConfiguration;
    }
+
+   static PersistedUser newUserEncoding(long id, ActiveMQBuffer buffer) {
+      PersistedUser persistedUser = new PersistedUser();
+      persistedUser.decode(buffer);
+      persistedUser.setStoreId(id);
+      return persistedUser;
+   }
+
+   static PersistedRole newRoleEncoding(long id, ActiveMQBuffer buffer) {
+      PersistedRole persistedRole = new PersistedRole();
+      persistedRole.decode(buffer);
+      persistedRole.setStoreId(id);
+      return persistedRole;
+   }
    /**
     * @param id
     * @param buffer
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java
index 3ac704e..d4e3cde 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/DescribeJournal.java
@@ -88,9 +88,11 @@ import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalR
 import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.PAGE_TRANSACTION;
 import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.QUEUE_BINDING_RECORD;
 import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.QUEUE_STATUS_RECORD;
-import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.SECURITY_RECORD;
+import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.ROLE_RECORD;
+import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.SECURITY_SETTING_RECORD;
 import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.SET_SCHEDULED_DELIVERY_TIME;
 import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.UPDATE_DELIVERY_COUNT;
+import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.USER_RECORD;
 
 /**
  * Outputs a String description of the Journals contents.
@@ -685,7 +687,7 @@ public final class DescribeJournal {
          case ADDRESS_SETTING_RECORD:
             return AbstractJournalStorageManager.newAddressEncoding(id, buffer);
 
-         case SECURITY_RECORD:
+         case SECURITY_SETTING_RECORD:
             return AbstractJournalStorageManager.newSecurityRecord(id, buffer);
 
          case ADDRESS_BINDING_RECORD:
@@ -694,6 +696,12 @@ public final class DescribeJournal {
          case ADDRESS_STATUS_RECORD:
             return AbstractJournalStorageManager.newAddressStatusEncoding(id, buffer);
 
+         case USER_RECORD:
+            return AbstractJournalStorageManager.newUserEncoding(id, buffer);
+
+         case ROLE_RECORD:
+            return AbstractJournalStorageManager.newRoleEncoding(id, buffer);
+
          default:
             return null;
       }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalRecordIds.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalRecordIds.java
index b18d360..5644324 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalRecordIds.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/journal/JournalRecordIds.java
@@ -45,7 +45,7 @@ public final class JournalRecordIds {
 
    public static final byte ADDRESS_SETTING_RECORD = 25;
 
-   public static final byte SECURITY_RECORD = 26;
+   public static final byte SECURITY_SETTING_RECORD = 26;
 
    public static final byte DIVERT_RECORD = 27;
 
@@ -92,4 +92,8 @@ public final class JournalRecordIds {
 
    public static final byte ADDRESS_STATUS_RECORD = 46;
 
+   public static final byte USER_RECORD = 47;
+
+   public static final byte ROLE_RECORD = 48;
+
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageManager.java
index 2660b0d..d1783b2 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageManager.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/impl/nullpm/NullStorageManager.java
@@ -49,7 +49,9 @@ import org.apache.activemq.artemis.core.persistence.AddressQueueStatus;
 import org.apache.activemq.artemis.core.persistence.StorageManager;
 import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
 import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
-import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
+import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
+import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
+import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
 import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
 import org.apache.activemq.artemis.core.postoffice.Binding;
 import org.apache.activemq.artemis.core.postoffice.PostOffice;
@@ -438,7 +440,7 @@ public class NullStorageManager implements StorageManager {
    }
 
    @Override
-   public List<PersistedRoles> recoverPersistedRoles() throws Exception {
+   public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
       return Collections.emptyList();
    }
 
@@ -456,7 +458,33 @@ public class NullStorageManager implements StorageManager {
    }
 
    @Override
-   public void storeSecurityRoles(final PersistedRoles persistedRoles) throws Exception {
+   public void storeUser(PersistedUser persistedUser) throws Exception {
+   }
+
+   @Override
+   public void deleteUser(String username) throws Exception {
+   }
+
+   @Override
+   public Map<String, PersistedUser> getPersistedUsers() {
+      return null;
+   }
+
+   @Override
+   public void storeRole(PersistedRole persistedRole) throws Exception {
+   }
+
+   @Override
+   public void deleteRole(String role) throws Exception {
+   }
+
+   @Override
+   public Map<String, PersistedRole> getPersistedRoles() {
+      return null;
+   }
+
+   @Override
+   public void storeSecuritySetting(final PersistedSecuritySetting persistedRoles) throws Exception {
    }
 
    @Override
@@ -464,7 +492,7 @@ public class NullStorageManager implements StorageManager {
    }
 
    @Override
-   public void deleteSecurityRoles(final SimpleString addressMatch) throws Exception {
+   public void deleteSecuritySetting(final SimpleString addressMatch) throws Exception {
    }
 
    @Override
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/User.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/User.java
index 9e6bacb..3fa4761 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/User.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/User.java
@@ -16,11 +16,13 @@
  */
 package org.apache.activemq.artemis.core.security;
 
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
+
 public class User {
 
    final String user;
 
-   final String password;
+   String password;
 
    public User(final String user, final String password) {
       this.user = user;
@@ -54,7 +56,8 @@ public class User {
       if (user == null) {
          return false;
       }
-      return this.user.equals(user) && this.password.equals(password);
+
+      return this.user.equals(user) && PasswordMaskingUtil.getHashProcessor(this.password).compare(password != null ? password.toCharArray() : null, this.password);
    }
 
    public String getUser() {
@@ -64,4 +67,8 @@ public class User {
    public String getPassword() {
       return password;
    }
+
+   public void setPassword(String password) {
+      this.password = password;
+   }
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQMessageBundle.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQMessageBundle.java
index b259194..3ae3eb9 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQMessageBundle.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQMessageBundle.java
@@ -495,4 +495,7 @@ public interface ActiveMQMessageBundle {
 
    @Message(id = 229232, value = "Cannot create consumer on {0}. Session is closed.", format = Message.Format.MESSAGE_FORMAT)
    ActiveMQIllegalStateException cannotCreateConsumerOnClosedSession(SimpleString queueName);
+
+   @Message(id = 229233, value = "Cannot set ActiveMQSecurityManager during startup or while started")
+   IllegalStateException cannotSetSecurityManager();
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java
index 77ff502..7b65bf2 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServer.java
@@ -833,6 +833,10 @@ public interface ActiveMQServer extends ServiceComponent {
 
    void setMBeanServer(MBeanServer mBeanServer);
 
+   MBeanServer getMBeanServer();
+
+   void setSecurityManager(ActiveMQSecurityManager securityManager);
+
    /**
     * Adding external components is allowed only if the state
     * isn't {@link SERVER_STATE#STOPPED} or {@link SERVER_STATE#STOPPING}.<br>
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java
index d95e3a1..1d6b8dd 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/ActiveMQServerLogger.java
@@ -1717,6 +1717,14 @@ public interface ActiveMQServerLogger extends BasicLogger {
          format = Message.Format.MESSAGE_FORMAT)
    void unableStartManagementContext(@Cause Exception e);
 
+   @LogMessage(level = Logger.Level.WARN)
+   @Message(id = 222298, value = "Failed to create bootstrap user \"{0}\". User management may not function.", format = Message.Format.MESSAGE_FORMAT)
+   void failedToCreateBootstrapCredentials(@Cause Exception e, String user);
+
+   @LogMessage(level = Logger.Level.WARN)
+   @Message(id = 222299, value = "No bootstrap credentials found. User management may not function.", format = Message.Format.MESSAGE_FORMAT)
+   void noBootstrapCredentialsFound();
+
    @LogMessage(level = Logger.Level.ERROR)
    @Message(id = 224000, value = "Failure in initialisation", format = Message.Format.MESSAGE_FORMAT)
    void initializationError(@Cause Throwable e);
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java
index 4591b30..c538569 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ActiveMQServerImpl.java
@@ -94,7 +94,7 @@ import org.apache.activemq.artemis.core.persistence.QueueBindingInfo;
 import org.apache.activemq.artemis.core.persistence.StorageManager;
 import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
 import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
-import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
+import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
 import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
 import org.apache.activemq.artemis.core.persistence.impl.journal.JDBCJournalStorageManager;
 import org.apache.activemq.artemis.core.persistence.impl.journal.JournalStorageManager;
@@ -186,6 +186,7 @@ import org.apache.activemq.artemis.logs.AuditLogger;
 import org.apache.activemq.artemis.spi.core.protocol.ProtocolManagerFactory;
 import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
 import org.apache.activemq.artemis.spi.core.protocol.SessionCallback;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
 import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
 import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
 import org.apache.activemq.artemis.utils.ActiveMQThreadPoolExecutor;
@@ -236,7 +237,7 @@ public class ActiveMQServerImpl implements ActiveMQServer {
 
    private final Version version;
 
-   private final ActiveMQSecurityManager securityManager;
+   private ActiveMQSecurityManager securityManager;
 
    private final Configuration configuration;
 
@@ -869,6 +870,19 @@ public class ActiveMQServerImpl implements ActiveMQServer {
       this.mbeanServer = mbeanServer;
    }
 
+   @Override
+   public MBeanServer getMBeanServer() {
+      return mbeanServer;
+   }
+
+   @Override
+   public void setSecurityManager(ActiveMQSecurityManager securityManager) {
+      if (state == SERVER_STATE.STARTING || state == SERVER_STATE.STARTED) {
+         throw ActiveMQMessageBundle.BUNDLE.cannotSetSecurityManager();
+      }
+      this.securityManager = securityManager;
+   }
+
    private void validateAddExternalComponent(ActiveMQComponent externalComponent) {
       final SERVER_STATE state = this.state;
       if (state == SERVER_STATE.STOPPED || state == SERVER_STATE.STOPPING) {
@@ -3048,6 +3062,10 @@ public class ActiveMQServerImpl implements ActiveMQServer {
 
       removeExtraAddressStores();
 
+      if (securityManager instanceof ActiveMQBasicSecurityManager) {
+         ((ActiveMQBasicSecurityManager)securityManager).completeInit(storageManager);
+      }
+
       final ServerInfo dumper = new ServerInfo(this, pagingManager);
 
       long dumpInfoInterval = configuration.getServerDumpInterval();
@@ -3381,6 +3399,8 @@ public class ActiveMQServerImpl implements ActiveMQServer {
          }
       }
 
+      // TODO load users/roles
+
       journalLoader.cleanUp();
 
       return journalInfo;
@@ -3395,9 +3415,9 @@ public class ActiveMQServerImpl implements ActiveMQServer {
          addressSettingsRepository.addMatch(set.getAddressMatch().toString(), set.getSetting());
       }
 
-      List<PersistedRoles> roles = storageManager.recoverPersistedRoles();
+      List<PersistedSecuritySetting> roles = storageManager.recoverSecuritySettings();
 
-      for (PersistedRoles roleItem : roles) {
+      for (PersistedSecuritySetting roleItem : roles) {
          Set<Role> setRoles = SecurityFormatter.createSecurity(roleItem.getSendRoles(), roleItem.getConsumeRoles(), roleItem.getCreateDurableQueueRoles(), roleItem.getDeleteDurableQueueRoles(), roleItem.getCreateNonDurableQueueRoles(), roleItem.getDeleteNonDurableQueueRoles(), roleItem.getManageRoles(), roleItem.getBrowseRoles(), roleItem.getCreateAddressRoles(), roleItem.getDeleteAddressRoles());
 
          securityRepository.addMatch(roleItem.getAddressMatch().toString(), setRoles);
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/BasicAuthenticator.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/BasicAuthenticator.java
new file mode 100644
index 0000000..eda88a4
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/BasicAuthenticator.java
@@ -0,0 +1,56 @@
+/*
+ * 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.server.management;
+
+import javax.management.remote.JMXAuthenticator;
+import javax.security.auth.Subject;
+
+import org.apache.activemq.artemis.logs.AuditLogger;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
+
+/**
+ * JMXAuthenticator implementation to be used with ActiveMQBasicSecurityManager
+ */
+public class BasicAuthenticator implements JMXAuthenticator {
+
+   ActiveMQBasicSecurityManager securityManager;
+
+   public BasicAuthenticator(ActiveMQBasicSecurityManager securityManager) {
+      this.securityManager = securityManager;
+   }
+
+   @Override
+   public Subject authenticate(final Object credentials) throws SecurityException {
+      Subject result;
+      String[] params = null;
+      if (credentials instanceof String[] && ((String[]) credentials).length == 2) {
+         params = (String[]) credentials;
+      }
+      result = securityManager.authenticate(params[0], params[1], null, null);
+      if (result != null) {
+         if (AuditLogger.isResourceLoggingEnabled()) {
+            AuditLogger.userSuccesfullyLoggedInAudit(result);
+         }
+         return result;
+      } else {
+         if (AuditLogger.isResourceLoggingEnabled()) {
+            AuditLogger.userFailedLoggedInAudit(result, null);
+         }
+         throw new SecurityException("Authentication failed");
+      }
+   }
+}
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementConnector.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementConnector.java
index 36113c6..a73c3f1 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementConnector.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementConnector.java
@@ -19,9 +19,12 @@ package org.apache.activemq.artemis.core.server.management;
 import org.apache.activemq.artemis.core.config.JMXConnectorConfiguration;
 import org.apache.activemq.artemis.core.server.ActiveMQComponent;
 import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
 
 import javax.management.MBeanServer;
 import javax.management.ObjectName;
+import javax.management.remote.JMXAuthenticator;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -32,9 +35,11 @@ public class ManagementConnector implements ActiveMQComponent {
    private ConnectorServerFactory connectorServerFactory;
    private RmiRegistryFactory rmiRegistryFactory;
    private MBeanServerFactory mbeanServerFactory;
+   private ActiveMQSecurityManager securityManager;
 
-   public ManagementConnector(JMXConnectorConfiguration configuration) {
+   public ManagementConnector(JMXConnectorConfiguration configuration, ActiveMQSecurityManager securityManager) {
       this.configuration = configuration;
+      this.securityManager = securityManager;
    }
 
    @Override
@@ -54,8 +59,15 @@ public class ManagementConnector implements ActiveMQComponent {
 
       MBeanServer mbeanServer = mbeanServerFactory.getServer();
 
-      JaasAuthenticator jaasAuthenticator = new JaasAuthenticator();
-      jaasAuthenticator.setRealm(configuration.getJmxRealm());
+      JMXAuthenticator authenticator;
+
+      if (securityManager != null && securityManager instanceof ActiveMQBasicSecurityManager) {
+         authenticator = new BasicAuthenticator((ActiveMQBasicSecurityManager) securityManager);
+      } else {
+         JaasAuthenticator jaasAuthenticator = new JaasAuthenticator();
+         jaasAuthenticator.setRealm(configuration.getJmxRealm());
+         authenticator = jaasAuthenticator;
+      }
 
       connectorServerFactory = new ConnectorServerFactory();
       connectorServerFactory.setServer(mbeanServer);
@@ -63,7 +75,7 @@ public class ManagementConnector implements ActiveMQComponent {
       connectorServerFactory.setRmiServerHost(configuration.getConnectorHost());
       connectorServerFactory.setObjectName(new ObjectName(configuration.getObjectName()));
       Map<String, Object> environment = new HashMap<>();
-      environment.put("jmx.remote.authenticator", jaasAuthenticator);
+      environment.put("jmx.remote.authenticator", authenticator);
       try {
          connectorServerFactory.setEnvironment(environment);
          connectorServerFactory.setAuthenticatorType(configuration.getAuthenticatorType());
@@ -108,4 +120,7 @@ public class ManagementConnector implements ActiveMQComponent {
       }
    }
 
+   public ConnectorServerFactory getConnectorServerFactory() {
+      return connectorServerFactory;
+   }
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java
index 1476e4e..42fa2fd 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java
@@ -25,6 +25,7 @@ import org.apache.activemq.artemis.core.server.ServiceComponent;
 import org.apache.activemq.artemis.core.server.management.impl.HawtioSecurityControlImpl;
 
 import javax.management.NotCompliantMBeanException;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
 
 public class ManagementContext implements ServiceComponent {
    private AtomicBoolean isStarted = new AtomicBoolean(false);
@@ -32,6 +33,7 @@ public class ManagementContext implements ServiceComponent {
    private JMXConnectorConfiguration jmxConnectorConfiguration;
    private ManagementConnector mBeanServer;
    private ArtemisMBeanServerGuard guardHandler;
+   private ActiveMQSecurityManager securityManager;
 
    @Override
    public void start() throws Exception {
@@ -44,7 +46,7 @@ public class ManagementContext implements ServiceComponent {
       }
 
       if (jmxConnectorConfiguration != null) {
-         mBeanServer = new ManagementConnector(jmxConnectorConfiguration);
+         mBeanServer = new ManagementConnector(jmxConnectorConfiguration, securityManager);
          mBeanServer.start();
       }
       isStarted.set(true);
@@ -99,4 +101,16 @@ public class ManagementContext implements ServiceComponent {
    public ArtemisMBeanServerGuard getArtemisMBeanServerGuard() {
       return guardHandler;
    }
+
+   public void setSecurityManager(ActiveMQSecurityManager securityManager) {
+      this.securityManager = securityManager;
+   }
+
+   public ActiveMQSecurityManager getSecurityManager() {
+      return securityManager;
+   }
+
+   public ManagementConnector getManagementConnector() {
+      return mBeanServer;
+   }
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQBasicSecurityManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQBasicSecurityManager.java
new file mode 100644
index 0000000..e6558a7
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQBasicSecurityManager.java
@@ -0,0 +1,211 @@
+/*
+ * 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 java.security.Principal;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.activemq.artemis.core.persistence.StorageManager;
+import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
+import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
+import org.apache.activemq.artemis.core.security.CheckType;
+import org.apache.activemq.artemis.core.security.Role;
+import org.apache.activemq.artemis.core.security.User;
+import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
+import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
+import org.apache.activemq.artemis.logs.AuditLogger;
+import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
+import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
+import org.apache.activemq.artemis.utils.SecurityManagerUtil;
+import org.jboss.logging.Logger;
+
+/**
+ * All user and role state (both in memory and on disk) is maintained by the underlying StorageManager
+ */
+public class ActiveMQBasicSecurityManager implements ActiveMQSecurityManager5, UserManagement {
+
+   private static final Logger logger = Logger.getLogger(ActiveMQBasicSecurityManager.class);
+
+   public static final String BOOTSTRAP_USER = "bootstrapUser";
+   public static final String BOOTSTRAP_PASSWORD = "bootstrapPassword";
+   public static final String BOOTSTRAP_ROLE = "bootstrapRole";
+
+   private Map<String, String> properties;
+   private String rolePrincipalClass = RolePrincipal.class.getName();
+   private StorageManager storageManager;
+
+   @Override
+   public ActiveMQBasicSecurityManager init(Map<String, String> properties) {
+      if (!properties.containsKey(BOOTSTRAP_USER) || !properties.containsKey(BOOTSTRAP_PASSWORD) || !properties.containsKey(BOOTSTRAP_ROLE)) {
+         ActiveMQServerLogger.LOGGER.noBootstrapCredentialsFound();
+      } else {
+         this.properties = properties;
+      }
+      return this;
+   }
+
+   @Override
+   public boolean validateUser(String user, String password) {
+      throw new UnsupportedOperationException("Invoke authenticate(String, String, RemotingConnection, String) instead");
+   }
+
+   @Override
+   public Subject authenticate(final String userToAuthenticate, final String passwordToAuthenticate, RemotingConnection remotingConnection, final String securityDomain) {
+      try {
+         if (storageManager.isStarted() && storageManager.getPersistedUsers() != null) {
+            PersistedUser persistedUser = storageManager.getPersistedUsers().get(userToAuthenticate);
+            if (persistedUser != null) {
+               User user = new User(persistedUser.getUsername(), persistedUser.getPassword());
+               if (user.isValid(userToAuthenticate, passwordToAuthenticate)) {
+                  Subject subject = new Subject();
+                  subject.getPrincipals().add(new UserPrincipal(userToAuthenticate));
+                  for (String role : getRole(userToAuthenticate).getRoles()) {
+                     subject.getPrincipals().add((Principal) SecurityManagerUtil.createGroupPrincipal(role, rolePrincipalClass));
+                  }
+                  if (AuditLogger.isAnyLoggingEnabled() && remotingConnection != null) {
+                     remotingConnection.setAuditSubject(subject);
+                  }
+                  if (AuditLogger.isResourceLoggingEnabled()) {
+                     AuditLogger.userSuccesfullyLoggedInAudit(subject);
+                  }
+                  return subject;
+               }
+            }
+         }
+      } catch (Exception e) {
+         if (logger.isDebugEnabled()) {
+            logger.debug("Couldn't validate user", e);
+         }
+      }
+
+      return null;
+   }
+
+   @Override
+   public boolean validateUserAndRole(String user, String password, Set<Role> roles, CheckType checkType) {
+      throw new UnsupportedOperationException("Invoke authorize(Subject, Set<Role>, CheckType, String) instead");
+   }
+
+   @Override
+   public boolean authorize(final Subject subject,
+                            final Set<Role> roles,
+                            final CheckType checkType,
+                            final String address) {
+      boolean authorized = SecurityManagerUtil.authorize(subject, roles, checkType, rolePrincipalClass);
+
+      if (logger.isTraceEnabled()) {
+         logger.trace("user " + (authorized ? " is " : " is NOT ") + "authorized");
+      }
+
+      return authorized;
+   }
+
+   @Override
+   public synchronized void addNewUser(String user, String password, String... roles) throws Exception {
+      if (user == null) {
+         throw ActiveMQMessageBundle.BUNDLE.nullUser();
+      }
+      if (password == null) {
+         throw ActiveMQMessageBundle.BUNDLE.nullPassword();
+      }
+      if (userExists(user)) {
+         throw ActiveMQMessageBundle.BUNDLE.userAlreadyExists(user);
+      }
+
+      storageManager.storeUser(new PersistedUser(user, password));
+      storageManager.storeRole(new PersistedRole(user, Arrays.asList(roles)));
+   }
+
+   @Override
+   public synchronized void removeUser(final String user) throws Exception {
+      if (!userExists(user)) {
+         throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(user);
+      }
+
+      storageManager.deleteUser(user);
+      storageManager.deleteRole(user);
+   }
+
+   @Override
+   public synchronized Map<String, Set<String>> listUser(String user) {
+      // a null or empty user is actually valid here
+      if (user != null && user.length() != 0 && !userExists(user)) {
+         throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(user);
+      }
+
+      Map<String, Set<String>> result = new HashMap<>();
+
+      if (user != null && user.length() > 0) {
+         result.put(user, new HashSet<>(getRole(user).getRoles()));
+      } else {
+         for (String thisUser : storageManager.getPersistedUsers().keySet()) {
+            result.put(thisUser, new HashSet<>(getRole(thisUser).getRoles()));
+         }
+      }
+      return result;
+   }
+
+   @Override
+   public synchronized void updateUser(String user, String password, String... roles) throws Exception {
+      if (!userExists(user)) {
+         throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(user);
+      }
+
+      // potentially update the user's password
+      if (password != null) {
+         storageManager.deleteUser(user);
+         storageManager.storeUser(new PersistedUser(user, password));
+      }
+
+      // potentially update the user's role(s)
+      if (roles != null && roles.length > 0) {
+         storageManager.deleteRole(user);
+         storageManager.storeRole(new PersistedRole(user, Arrays.asList(roles)));
+      }
+   }
+
+   public void completeInit(StorageManager storageManager) {
+      this.storageManager = storageManager;
+
+      // add/update the bootstrap user now that the StorageManager is set
+      if (properties != null && properties.containsKey(BOOTSTRAP_USER) && properties.containsKey(BOOTSTRAP_PASSWORD) && properties.containsKey(BOOTSTRAP_ROLE)) {
+         try {
+            if (userExists(properties.get(BOOTSTRAP_USER))) {
+               updateUser(properties.get(BOOTSTRAP_USER), properties.get(BOOTSTRAP_PASSWORD), new String[]{properties.get(BOOTSTRAP_ROLE)});
+            } else {
+               addNewUser(properties.get(BOOTSTRAP_USER), properties.get(BOOTSTRAP_PASSWORD), new String[]{properties.get(BOOTSTRAP_ROLE)});
+            }
+         } catch (Exception e) {
+            ActiveMQServerLogger.LOGGER.failedToCreateBootstrapCredentials(e, properties.get(BOOTSTRAP_USER));
+         }
+      }
+   }
+
+   private boolean userExists(String user) {
+      return user != null && storageManager.getPersistedUsers() != null && storageManager.getPersistedUsers().containsKey(user);
+   }
+
+   private PersistedRole getRole(String user) {
+      return storageManager.getPersistedRoles().get(user);
+   }
+}
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
index 18fada5..d5129a2 100644
--- 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
@@ -19,21 +19,16 @@ 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.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.security.Principal;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Set;
 
 import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
 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.logs.AuditLogger;
 import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
 import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler;
 import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
+import org.apache.activemq.artemis.utils.SecurityManagerUtil;
 import org.jboss.logging.Logger;
 
 import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getCertsFromConnection;
@@ -48,8 +43,6 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
 
    private static final Logger logger = Logger.getLogger(ActiveMQJAASSecurityManager.class);
 
-   private static final String WILDCARD = "*";
-
    private String configurationName;
    private String certificateConfigurationName;
    private SecurityConfiguration configuration;
@@ -115,33 +108,10 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
                             final Set<Role> roles,
                             final CheckType checkType,
                             final String address) {
-      boolean authorized = false;
-
-      if (subject != null) {
-         Set<RolePrincipal> rolesWithPermission = getPrincipalsInRole(checkType, roles);
-
-         // Check the caller's roles
-         Set<Principal> rolesForSubject = new HashSet<>();
-         try {
-            rolesForSubject.addAll(subject.getPrincipals(Class.forName(rolePrincipalClass).asSubclass(Principal.class)));
-         } catch (Exception e) {
-            ActiveMQServerLogger.LOGGER.failedToFindRolesForTheSubject(e);
-         }
-         if (rolesForSubject.size() > 0 && rolesWithPermission.size() > 0) {
-            Iterator<Principal> rolesForSubjectIter = rolesForSubject.iterator();
-            while (!authorized && rolesForSubjectIter.hasNext()) {
-               Iterator<RolePrincipal> rolesWithPermissionIter = rolesWithPermission.iterator();
-               Principal subjectRole = rolesForSubjectIter.next();
-               while (!authorized && rolesWithPermissionIter.hasNext()) {
-                  Principal roleWithPermission = rolesWithPermissionIter.next();
-                  authorized = subjectRole.equals(roleWithPermission);
-               }
-            }
-         }
+      boolean authorized = SecurityManagerUtil.authorize(subject, roles, checkType, rolePrincipalClass);
 
-         if (logger.isTraceEnabled()) {
-            logger.trace("user " + (authorized ? " is " : " is NOT ") + "authorized");
-         }
+      if (logger.isTraceEnabled()) {
+         logger.trace("user " + (authorized ? " is " : " is NOT ") + "authorized");
       }
 
       return authorized;
@@ -187,20 +157,6 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
       }
    }
 
-   private Set<RolePrincipal> getPrincipalsInRole(final CheckType checkType, final Set<Role> roles) {
-      Set principals = new HashSet<>();
-      for (Role role : roles) {
-         if (checkType.hasRole(role)) {
-            try {
-               principals.add(createGroupPrincipal(role.getName(), rolePrincipalClass));
-            } catch (Exception e) {
-               ActiveMQServerLogger.LOGGER.failedAddRolePrincipal(e);
-            }
-         }
-      }
-      return principals;
-   }
-
    public void setConfigurationName(final String configurationName) {
       this.configurationName = configurationName;
    }
@@ -240,60 +196,4 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
    public void setRolePrincipalClass(String rolePrincipalClass) {
       this.rolePrincipalClass = rolePrincipalClass;
    }
-
-   public static Object createGroupPrincipal(String name, String groupClass) throws Exception {
-      if (WILDCARD.equals(name)) {
-         // simple match all group principal - match any name and class
-         return new Principal() {
-            @Override
-            public String getName() {
-               return WILDCARD;
-            }
-
-            @Override
-            public boolean equals(Object other) {
-               return true;
-            }
-
-            @Override
-            public int hashCode() {
-               return WILDCARD.hashCode();
-            }
-         };
-      }
-      Object[] param = new Object[]{name};
-
-      Class<?> cls = Class.forName(groupClass);
-
-      Constructor<?>[] constructors = cls.getConstructors();
-      int i;
-      Object instance;
-      for (i = 0; i < constructors.length; i++) {
-         Class<?>[] paramTypes = constructors[i].getParameterTypes();
-         if (paramTypes.length != 0 && paramTypes[0].equals(String.class)) {
-            break;
-         }
-      }
-      if (i < constructors.length) {
-         instance = constructors[i].newInstance(param);
-      } else {
-         instance = cls.newInstance();
-         Method[] methods = cls.getMethods();
-         i = 0;
-         for (i = 0; i < methods.length; i++) {
-            Class<?>[] paramTypes = methods[i].getParameterTypes();
-            if (paramTypes.length != 0 && methods[i].getName().equals("setName") && paramTypes[0].equals(String.class)) {
-               break;
-            }
-         }
-
-         if (i < methods.length) {
-            methods[i].invoke(instance, param);
-         } else {
-            throw new NoSuchMethodException();
-         }
-      }
-
-      return instance;
-   }
 }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/UserManagement.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/UserManagement.java
new file mode 100644
index 0000000..743f86f
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/UserManagement.java
@@ -0,0 +1,31 @@
+/*
+ * 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.util.Map;
+import java.util.Set;
+
+public interface UserManagement {
+
+   void addNewUser(String user, String password, String... roles) throws Exception;
+
+   void removeUser(String user) throws Exception;
+
+   Map<String, Set<String>> listUser(String user);
+
+   void updateUser(String username, String password, String... roles) throws Exception;
+}
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
index 392fdc7..212dbb3 100644
--- 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
@@ -148,7 +148,7 @@ public class LDAPLoginModule implements AuditLoginModule {
 
    private String getPlainPassword(String password) {
       try {
-         return PasswordMaskingUtil.resolveMask(null, password, codecClass);
+         return PasswordMaskingUtil.resolveMask(password, codecClass);
       } catch (Exception e) {
          throw new IllegalArgumentException("Failed to decode password", e);
       }
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModuleConfigurator.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModuleConfigurator.java
index 245360c..21c238a 100644
--- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModuleConfigurator.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModuleConfigurator.java
@@ -29,6 +29,7 @@ import java.util.Set;
 
 import org.apache.activemq.artemis.api.core.Pair;
 import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
+import org.apache.activemq.artemis.spi.core.security.UserManagement;
 import org.apache.activemq.artemis.utils.StringUtil;
 import org.apache.commons.configuration2.PropertiesConfiguration;
 import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
@@ -37,7 +38,7 @@ import org.apache.commons.configuration2.builder.fluent.Configurations;
 import static org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule.ROLE_FILE_PROP_NAME;
 import static org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule.USER_FILE_PROP_NAME;
 
-public class PropertiesLoginModuleConfigurator {
+public class PropertiesLoginModuleConfigurator implements UserManagement {
 
    private static final String LICENSE_HEADER =
            "## ---------------------------------------------------------------------------\n" +
@@ -125,7 +126,8 @@ public class PropertiesLoginModuleConfigurator {
       }
    }
 
-   public void addNewUser(String username, String hash, String... roles) throws Exception {
+   @Override
+   public void addNewUser(String username, String hash, String... roles) {
       if (userConfig.getString(username) != null) {
          throw ActiveMQMessageBundle.BUNDLE.userAlreadyExists(username);
       }
@@ -143,6 +145,7 @@ public class PropertiesLoginModuleConfigurator {
       }
    }
 
+   @Override
    public void removeUser(String username) {
       if (userConfig.getProperty(username) == null) {
          throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
@@ -151,6 +154,7 @@ public class PropertiesLoginModuleConfigurator {
       removeRoles(username);
    }
 
+   @Override
    public Map<String, Set<String>> listUser(String username) {
       Map<String, Set<String>> result = new HashMap<>();
 
@@ -166,7 +170,8 @@ public class PropertiesLoginModuleConfigurator {
       return result;
    }
 
-   public void updateUser(String username, String password, String[] roles) {
+   @Override
+   public void updateUser(String username, String password, String... roles) {
       String oldPassword = (String) userConfig.getProperty(username);
       if (oldPassword == null) {
          throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/utils/SecurityManagerUtil.java b/artemis-server/src/main/java/org/apache/activemq/artemis/utils/SecurityManagerUtil.java
new file mode 100644
index 0000000..bb8d90f
--- /dev/null
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/utils/SecurityManagerUtil.java
@@ -0,0 +1,138 @@
+/**
+ * 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.utils;
+
+import javax.security.auth.Subject;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+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.RolePrincipal;
+
+public class SecurityManagerUtil {
+
+   private static final String WILDCARD = "*";
+
+   public static Set<RolePrincipal> getPrincipalsInRole(final CheckType checkType, final Set<Role> roles, final String rolePrincipalClass) {
+      Set principals = new HashSet<>();
+      for (Role role : roles) {
+         if (checkType.hasRole(role)) {
+            try {
+               principals.add(SecurityManagerUtil.createGroupPrincipal(role.getName(), rolePrincipalClass));
+            } catch (Exception e) {
+               ActiveMQServerLogger.LOGGER.failedAddRolePrincipal(e);
+            }
+         }
+      }
+      return principals;
+   }
+
+   public static Object createGroupPrincipal(String name, String groupClass) throws Exception {
+      if (WILDCARD.equals(name)) {
+         // simple match all group principal - match any name and class
+         return new Principal() {
+            @Override
+            public String getName() {
+               return WILDCARD;
+            }
+
+            @Override
+            public boolean equals(Object other) {
+               return true;
+            }
+
+            @Override
+            public int hashCode() {
+               return WILDCARD.hashCode();
+            }
+         };
+      }
+      Object[] param = new Object[]{name};
+
+      Class<?> cls = Class.forName(groupClass);
+
+      Constructor<?>[] constructors = cls.getConstructors();
+      int i;
+      Object instance;
+      for (i = 0; i < constructors.length; i++) {
+         Class<?>[] paramTypes = constructors[i].getParameterTypes();
+         if (paramTypes.length != 0 && paramTypes[0].equals(String.class)) {
+            break;
+         }
+      }
+      if (i < constructors.length) {
+         instance = constructors[i].newInstance(param);
+      } else {
+         instance = cls.newInstance();
+         Method[] methods = cls.getMethods();
+         i = 0;
+         for (i = 0; i < methods.length; i++) {
+            Class<?>[] paramTypes = methods[i].getParameterTypes();
+            if (paramTypes.length != 0 && methods[i].getName().equals("setName") && paramTypes[0].equals(String.class)) {
+               break;
+            }
+         }
+
+         if (i < methods.length) {
+            methods[i].invoke(instance, param);
+         } else {
+            throw new NoSuchMethodException();
+         }
+      }
+
+      return instance;
+   }
+
+   /**
+    * This method tries to match the RolePrincipals in the Subject with the provided Set of Roles and CheckType
+    */
+   public static boolean authorize(final Subject subject, final Set<Role> roles, final CheckType checkType, final String rolePrincipalClass) {
+      boolean authorized = false;
+
+      if (subject != null) {
+         Set<RolePrincipal> rolesWithPermission = getPrincipalsInRole(checkType, roles, rolePrincipalClass);
+
+         // Check the caller's roles
+         Set<Principal> rolesForSubject = new HashSet<>();
+         try {
+            rolesForSubject.addAll(subject.getPrincipals(Class.forName(rolePrincipalClass).asSubclass(Principal.class)));
+         } catch (Exception e) {
+            ActiveMQServerLogger.LOGGER.failedToFindRolesForTheSubject(e);
+         }
+         if (rolesForSubject.size() > 0 && rolesWithPermission.size() > 0) {
+            Iterator<Principal> rolesForSubjectIter = rolesForSubject.iterator();
+            while (!authorized && rolesForSubjectIter.hasNext()) {
+               Iterator<RolePrincipal> rolesWithPermissionIter = rolesWithPermission.iterator();
+               Principal subjectRole = rolesForSubjectIter.next();
+               while (!authorized && rolesWithPermissionIter.hasNext()) {
+                  Principal roleWithPermission = rolesWithPermissionIter.next();
+                  authorized = subjectRole.equals(roleWithPermission);
+               }
+            }
+         }
+      }
+
+      return authorized;
+   }
+}
diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/transaction/impl/TransactionImplTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/transaction/impl/TransactionImplTest.java
index db68346..1ff7892 100644
--- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/transaction/impl/TransactionImplTest.java
+++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/transaction/impl/TransactionImplTest.java
@@ -48,7 +48,9 @@ import org.apache.activemq.artemis.core.persistence.QueueBindingInfo;
 import org.apache.activemq.artemis.core.persistence.StorageManager;
 import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
 import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
-import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
+import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
+import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
+import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
 import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
 import org.apache.activemq.artemis.core.postoffice.Binding;
 import org.apache.activemq.artemis.core.postoffice.PostOffice;
@@ -597,17 +599,17 @@ public class TransactionImplTest extends ActiveMQTestBase {
       }
 
       @Override
-      public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
+      public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
 
       }
 
       @Override
-      public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
+      public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
 
       }
 
       @Override
-      public List<PersistedRoles> recoverPersistedRoles() throws Exception {
+      public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
          return null;
       }
 
@@ -627,6 +629,36 @@ public class TransactionImplTest extends ActiveMQTestBase {
       }
 
       @Override
+      public void storeUser(PersistedUser persistedUser) throws Exception {
+
+      }
+
+      @Override
+      public void deleteUser(String username) throws Exception {
+
+      }
+
+      @Override
+      public Map<String, PersistedUser> getPersistedUsers() {
+         return null;
+      }
+
+      @Override
+      public void storeRole(PersistedRole persistedRole) throws Exception {
+
+      }
+
+      @Override
+      public void deleteRole(String role) throws Exception {
+
+      }
+
+      @Override
+      public Map<String, PersistedRole> getPersistedRoles() {
+         return null;
+      }
+
+      @Override
       public long storePageCounter(long txID, long queueID, long value, long size) throws Exception {
          return 0;
       }
diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md
index 43d47a7..269ae6f 100644
--- a/docs/user-manual/en/security.md
+++ b/docs/user-manual/en/security.md
@@ -352,16 +352,23 @@ the Transport](configuring-transports.md).
 
 ## User credentials
 
-Apache ActiveMQ Artemis ships with two security manager implementations:
-
-- The legacy, deprecated `ActiveMQSecurityManager` that reads user credentials,
-  i.e. user names, passwords and role information from properties files on the
-  classpath called `artemis-users.properties` and `artemis-roles.properties`.
+Apache ActiveMQ Artemis ships with three security manager implementations:
 
 - The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any
   standard JAAS login module. Artemis ships with several login modules which
   will be discussed further down. This is the default security manager.
 
+- The `ActiveMQBasicSecurityManager` which doesn't use JAAS and only supports
+  auth via username & password credentials. It also supports adding, removing,
+  and updating users via the management API. All user & role data is stored
+  in the broker's bindings journal which means any changes made to a live
+  broker will be available on its backup.
+
+- The legacy, deprecated `ActiveMQSecurityManagerImpl` that reads user
+  credentials, i.e. user names, passwords and role information from properties
+  files on the classpath called `artemis-users.properties` and
+  `artemis-roles.properties`.
+
 ### JAAS Security Manager
 
 When using the Java Authentication and Authorization Service (JAAS) much of the
@@ -1099,6 +1106,90 @@ superseded by SASL GSSAPI. However, for clients that don't support SASL (core
 client), using TLS can provide Kerberos authentication over an *unsecure*
 channel.
 
+### Basic Security Manager
+
+As the name suggests, the `ActiveMQBasicSecurityManager` is _basic_. It is not
+pluggable like the JAAS security manager and it _only_ supports authentication
+via username and password credentials. Furthermore, the the Hawtio-based web
+console requires JAAS. Therefore you will *still need* to configure a
+`login.config` if you plan on using the web console. However, this security
+manager *may* still may have a couple of advantages depending on your use-case.
+
+All user & role data is stored in the bindings journal (or bindings table if
+using JDBC). The advantage here is that in a live/backup use-case any user
+management performed on the live broker will be reflected on the backup upon
+failover. Typically LDAP would be employed for this kind of use-case, but not
+everyone wants or is able to administer an independent LDAP server.
+
+User management is provided by the broker's management API. This includes the
+ability to add, list, update, and remove users & roles. As with all management
+functions, this is available via JMX, management messages, HTTP (via Jolokia),
+web console, etc. These functions are also available from the ActiveMQ Artemis
+command-line interface. Having the broker store this data directly means that
+it must be running in order to manage users. There is no way to modify the
+bindings data manually.
+
+To be clear, any management access via HTTP (e.g. web console or Jolokia) will
+go through Hawtio JAAS. MBean access via JConsole or other remote JMX tool will
+go through the basic security manager. Management messages will also go through
+the basic security manager.
+
+#### Configuration
+
+The configuration for the `ActiveMQBasicSecurityManager` happens in
+`bootstrap.xml` just like it does for all security manager implementations.
+Here's an example:
+
+```xml
+<broker xmlns="http://activemq.org/schema">
+
+   <security-manager class-name="org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager">
+      <property key="bootstrapUser" value="myUser"/>
+      <property key="bootstrapPassword" value="myPass"/>
+      <property key="bootstrapRole" value="myRole"/>
+   </security-manager>
+  
+   ...
+</broker>
+```
+
+Because the bindings data which holds the user & role data cannot be modified
+manually and because the broker must be running to manage users and because
+the broker often needs to be secured from first boot the
+`ActiveMQBasicSecurityManager` has 3 properties to define a user whose
+credentials can then be used to add other users.
+
+- `bootstrapUser` - the name of the bootstrap user
+- `bootstrapPassword` - the password for the bootstrap user; supports masking
+- `bootstrapRole` - the role of the bootstrap user
+
+The value specified in the `bootstrapRole` will need the following permissions
+on the `activemq.management` address:
+
+- `createNonDurableQueue`
+- `createAddress`
+- `consume`
+- `manage`
+- `send`
+
+For example:
+
+```xml
+<security-setting match="activemq.management.#">
+   <permission type="createNonDurableQueue" roles="myRole"/>
+   <permission type="createAddress" roles="myRole"/>
+   <permission type="consume" roles="myRole"/>
+   <permission type="manage" roles="myRole"/>
+   <permission type="send" roles="myRole"/>
+</security-setting>
+```
+
+> **Note:**
+>
+> If the 3 `bootstrap` properties are defined then those credentials will be
+> set whenever you start the broker no matter what changes may have been made
+> to them at runtime previously.
+
 ## Mapping external roles
 Roles from external authentication providers (i.e. LDAP) can be mapped to internally used roles. The is done through role-mapping entries in the security-settings block:
 
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SendAckFailTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SendAckFailTest.java
index 222a0bb..786de34 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SendAckFailTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/SendAckFailTest.java
@@ -62,7 +62,9 @@ import org.apache.activemq.artemis.core.persistence.AddressQueueStatus;
 import org.apache.activemq.artemis.core.persistence.StorageManager;
 import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
 import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
-import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
+import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
+import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
+import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
 import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
 import org.apache.activemq.artemis.core.postoffice.Binding;
 import org.apache.activemq.artemis.core.postoffice.PostOffice;
@@ -694,18 +696,18 @@ public class SendAckFailTest extends SpawnedTestBase {
       }
 
       @Override
-      public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
-         manager.storeSecurityRoles(persistedRoles);
+      public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
+         manager.storeSecuritySetting(persistedRoles);
       }
 
       @Override
-      public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
-         manager.deleteSecurityRoles(addressMatch);
+      public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
+         manager.deleteSecuritySetting(addressMatch);
       }
 
       @Override
-      public List<PersistedRoles> recoverPersistedRoles() throws Exception {
-         return manager.recoverPersistedRoles();
+      public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
+         return manager.recoverSecuritySettings();
       }
 
       @Override
@@ -724,6 +726,36 @@ public class SendAckFailTest extends SpawnedTestBase {
       }
 
       @Override
+      public void storeUser(PersistedUser persistedUser) throws Exception {
+         manager.storeUser(persistedUser);
+      }
+
+      @Override
+      public void deleteUser(String username) throws Exception {
+         manager.deleteUser(username);
+      }
+
+      @Override
+      public Map<String, PersistedUser> getPersistedUsers() {
+         return manager.getPersistedUsers();
+      }
+
+      @Override
+      public void storeRole(PersistedRole persistedRole) throws Exception {
+         manager.storeRole(persistedRole);
+      }
+
+      @Override
+      public void deleteRole(String role) throws Exception {
+         manager.deleteRole(role);
+      }
+
+      @Override
+      public Map<String, PersistedRole> getPersistedRoles() {
+         return manager.getPersistedRoles();
+      }
+
+      @Override
       public long storePageCounter(long txID, long queueID, long value, long size) throws Exception {
          return manager.storePageCounter(txID, queueID, value, size);
       }
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/RolesConfigurationStorageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/RolesConfigurationStorageTest.java
index b525e9d..66263e2 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/RolesConfigurationStorageTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/RolesConfigurationStorageTest.java
@@ -22,13 +22,13 @@ import java.util.Map;
 
 import org.apache.activemq.artemis.api.core.SimpleString;
 import org.apache.activemq.artemis.core.config.StoreConfiguration;
-import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
+import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
 import org.junit.Before;
 import org.junit.Test;
 
 public class RolesConfigurationStorageTest extends StorageManagerTestBase {
 
-   private Map<SimpleString, PersistedRoles> mapExpectedSets;
+   private Map<SimpleString, PersistedSecuritySetting> mapExpectedSets;
 
    public RolesConfigurationStorageTest(StoreConfiguration.StoreType storeType) {
       super(storeType);
@@ -41,18 +41,18 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
       mapExpectedSets = new HashMap<>();
    }
 
-   protected void addSetting(PersistedRoles setting) throws Exception {
+   protected void addSetting(PersistedSecuritySetting setting) throws Exception {
       mapExpectedSets.put(setting.getAddressMatch(), setting);
-      journal.storeSecurityRoles(setting);
+      journal.storeSecuritySetting(setting);
    }
 
    @Test
    public void testStoreSecuritySettings() throws Exception {
       createStorage();
 
-      addSetting(new PersistedRoles("a#", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
+      addSetting(new PersistedSecuritySetting("a#", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
 
-      addSetting(new PersistedRoles("a2", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
+      addSetting(new PersistedSecuritySetting("a2", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
 
       journal.stop();
 
@@ -62,9 +62,9 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
 
       checkSettings();
 
-      addSetting(new PersistedRoles("a2", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
+      addSetting(new PersistedSecuritySetting("a2", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
 
-      addSetting(new PersistedRoles("a3", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
+      addSetting(new PersistedSecuritySetting("a3", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
 
       checkSettings();
 
@@ -92,7 +92,7 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
 
       checkSettings();
 
-      addSetting(new PersistedRoles("a#", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
+      addSetting(new PersistedSecuritySetting("a#", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
 
       journal.stop();
 
@@ -112,12 +112,12 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
     * @throws Exception
     */
    private void checkSettings() throws Exception {
-      List<PersistedRoles> listSetting = journal.recoverPersistedRoles();
+      List<PersistedSecuritySetting> listSetting = journal.recoverSecuritySettings();
 
       assertEquals(mapExpectedSets.size(), listSetting.size());
 
-      for (PersistedRoles el : listSetting) {
-         PersistedRoles el2 = mapExpectedSets.get(el.getAddressMatch());
+      for (PersistedSecuritySetting el : listSetting) {
+         PersistedSecuritySetting el2 = mapExpectedSets.get(el.getAddressMatch());
 
          assertEquals(el, el2);
       }
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerFailoverTest.java
new file mode 100644
index 0000000..6f162d8
--- /dev/null
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerFailoverTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.tests.integration.security;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.activemq.artemis.api.core.ActiveMQException;
+import org.apache.activemq.artemis.api.core.TransportConfiguration;
+import org.apache.activemq.artemis.api.core.client.ClientSession;
+import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
+import org.apache.activemq.artemis.core.config.ha.SharedStoreMasterPolicyConfiguration;
+import org.apache.activemq.artemis.core.config.ha.SharedStoreSlavePolicyConfiguration;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
+import org.apache.activemq.artemis.tests.integration.cluster.failover.FailoverTestBase;
+import org.apache.activemq.artemis.tests.util.ReplicatedBackupUtils;
+import org.apache.activemq.artemis.tests.util.TransportConfigurationUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(value = Parameterized.class)
+public class BasicSecurityManagerFailoverTest extends FailoverTestBase {
+
+   private boolean replicated;
+
+   @Parameterized.Parameters(name = "replicated={0}")
+   public static Collection getParameters() {
+      return Arrays.asList(new Object[][]{{true}, {false}});
+   }
+
+   public BasicSecurityManagerFailoverTest(boolean replicated) {
+      this.replicated = replicated;
+   }
+
+   @Override
+   protected void createConfigs() throws Exception {
+      if (replicated) {
+         createReplicatedConfigs();
+      } else {
+         createSharedStoreConfigs();
+      }
+   }
+
+   protected void createSharedStoreConfigs() throws Exception {
+      nodeManager = createNodeManager();
+      TransportConfiguration liveConnector = getConnectorTransportConfiguration(true);
+      TransportConfiguration backupConnector = getConnectorTransportConfiguration(false);
+
+      backupConfig = super
+         .createDefaultInVMConfig()
+         .setSecurityEnabled(true)
+         .clearAcceptorConfigurations()
+         .addAcceptorConfiguration(getAcceptorTransportConfiguration(false))
+         .setHAPolicyConfiguration(new SharedStoreSlavePolicyConfiguration())
+         .addConnectorConfiguration(liveConnector.getName(), liveConnector)
+         .addConnectorConfiguration(backupConnector.getName(), backupConnector)
+         .addClusterConfiguration(createBasicClusterConfig(backupConnector.getName(), liveConnector.getName()));
+
+      backupServer = createTestableServer(backupConfig);
+
+      backupServer.getServer().setSecurityManager(new ActiveMQBasicSecurityManager());
+
+      liveConfig = super
+         .createDefaultInVMConfig()
+         .setSecurityEnabled(true)
+         .clearAcceptorConfigurations()
+         .addAcceptorConfiguration(getAcceptorTransportConfiguration(true))
+         .setHAPolicyConfiguration(new SharedStoreMasterPolicyConfiguration())
+         .addClusterConfiguration(createBasicClusterConfig(liveConnector.getName()))
+         .addConnectorConfiguration(liveConnector.getName(), liveConnector);
+
+      liveServer = createTestableServer(liveConfig);
+
+      liveServer.getServer().setSecurityManager(new ActiveMQBasicSecurityManager());
+   }
+
+   @Override
+   protected void createReplicatedConfigs() throws Exception {
+      final TransportConfiguration liveConnector = getConnectorTransportConfiguration(true);
+      final TransportConfiguration backupConnector = getConnectorTransportConfiguration(false);
+      final TransportConfiguration backupAcceptor = getAcceptorTransportConfiguration(false);
+
+      backupConfig = createDefaultInVMConfig();
+      liveConfig = createDefaultInVMConfig();
+
+      ReplicatedBackupUtils.configureReplicationPair(backupConfig, backupConnector, backupAcceptor, liveConfig, liveConnector, null);
+
+      backupConfig
+         .setSecurityEnabled(true)
+         .setBindingsDirectory(getBindingsDir(0, true))
+         .setJournalDirectory(getJournalDir(0, true))
+         .setPagingDirectory(getPageDir(0, true))
+         .setLargeMessagesDirectory(getLargeMessagesDir(0, true));
+
+      setupHAPolicyConfiguration();
+      nodeManager = createReplicatedBackupNodeManager(backupConfig);
+
+      backupServer = createTestableServer(backupConfig);
+
+      backupServer.getServer().setSecurityManager(new ActiveMQBasicSecurityManager());
+
+      liveConfig
+         .setSecurityEnabled(true)
+         .clearAcceptorConfigurations()
+         .addAcceptorConfiguration(getAcceptorTransportConfiguration(true));
+
+      liveServer = createTestableServer(liveConfig);
+
+      liveServer.getServer().setSecurityManager(new ActiveMQBasicSecurityManager());
+   }
+
+   @Override
+   protected TransportConfiguration getAcceptorTransportConfiguration(final boolean live) {
+      return TransportConfigurationUtils.getInVMAcceptor(live);
+   }
+
+   @Override
+   protected TransportConfiguration getConnectorTransportConfiguration(final boolean live) {
+      return TransportConfigurationUtils.getInVMConnector(live);
+   }
+
+   @Test
+   public void testFailover() throws Exception {
+
+      liveServer.getServer().getActiveMQServerControl().addUser("foo", "bar", "baz", false);
+
+      ClientSessionFactory cf = createSessionFactory(getServerLocator());
+      ClientSession session = null;
+
+      try {
+         session = cf.createSession("foo", "bar", false, true, true, false, 0);
+      } catch (ActiveMQException e) {
+         e.printStackTrace();
+         Assert.fail("should not throw exception");
+      }
+
+      crash(session);
+      waitForServerToStart(backupServer.getServer());
+
+      try {
+         cf = createSessionFactory(getServerLocator());
+         session = cf.createSession("foo", "bar", false, true, true, false, 0);
+      } catch (ActiveMQException e) {
+         e.printStackTrace();
+         Assert.fail("should not throw exception");
+      }
+   }
+}
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerTest.java
new file mode 100644
index 0000000..79855a0
--- /dev/null
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerTest.java
@@ -0,0 +1,312 @@
+/*
+ * 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.tests.integration.security;
+
+import java.lang.management.ManagementFactory;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.activemq.artemis.api.core.ActiveMQException;
+import org.apache.activemq.artemis.api.core.QueueConfiguration;
+import org.apache.activemq.artemis.api.core.RoutingType;
+import org.apache.activemq.artemis.api.core.SimpleString;
+import org.apache.activemq.artemis.api.core.client.ClientConsumer;
+import org.apache.activemq.artemis.api.core.client.ClientMessage;
+import org.apache.activemq.artemis.api.core.client.ClientProducer;
+import org.apache.activemq.artemis.api.core.client.ClientSession;
+import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
+import org.apache.activemq.artemis.api.core.client.ServerLocator;
+import org.apache.activemq.artemis.core.security.Role;
+import org.apache.activemq.artemis.core.server.ActiveMQServer;
+import org.apache.activemq.artemis.core.server.ActiveMQServers;
+import org.apache.activemq.artemis.core.server.impl.AddressInfo;
+import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
+import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class BasicSecurityManagerTest extends ActiveMQTestBase {
+
+   private ServerLocator locator;
+
+   @Override
+   @Before
+   public void setUp() throws Exception {
+      super.setUp();
+
+      locator = createInVMNonHALocator();
+   }
+
+   public ActiveMQServer initializeServer() throws Exception {
+      Map<String, String> initProperties = new HashMap<>();
+      initProperties.put(ActiveMQBasicSecurityManager.BOOTSTRAP_USER, "first");
+      initProperties.put(ActiveMQBasicSecurityManager.BOOTSTRAP_PASSWORD, "secret");
+      initProperties.put(ActiveMQBasicSecurityManager.BOOTSTRAP_ROLE, "programmers");
+      ActiveMQBasicSecurityManager securityManager = new ActiveMQBasicSecurityManager().init(initProperties);
+      ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, true));
+      return server;
+   }
+
+   @Test
+   public void testAuthenticationForBootstrapUser() throws Exception {
+      ActiveMQServer server = initializeServer();
+      server.start();
+      ClientSessionFactory cf = createSessionFactory(locator);
+
+      try {
+         ClientSession session = cf.createSession("first", "secret", false, true, true, false, 0);
+         session.close();
+      } catch (ActiveMQException e) {
+         e.printStackTrace();
+         Assert.fail("should not throw exception");
+      }
+   }
+
+   @Test
+   public void testAuthenticationForAddedUserHashed() throws Exception {
+      internalTestAuthenticationForAddedUser(false);
+   }
+
+   @Test
+   public void testAuthenticationForAddedUserPlainText() throws Exception {
+      internalTestAuthenticationForAddedUser(true);
+   }
+
+   private void internalTestAuthenticationForAddedUser(boolean plaintext) throws Exception {
+      ActiveMQServer server = initializeServer();
+      server.start();
+      ClientSessionFactory cf = createSessionFactory(locator);
+
+      server.getActiveMQServerControl().addUser("foo", "bar", "baz", plaintext);
+
+      try {
+         ClientSession session = cf.createSession("foo", "bar", false, true, true, false, 0);
+         session.close();
+      } catch (ActiveMQException e) {
+         e.printStackTrace();
+         Assert.fail("should not throw exception");
+      }
+   }
+
+   @Test
+   public void testWithValidatedUser() throws Exception {
+      ActiveMQServer server = initializeServer();
+      server.getConfiguration().setPopulateValidatedUser(true);
+      server.start();
+      Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true);
+      Set<Role> roles = new HashSet<>();
+      roles.add(role);
+      server.getSecurityRepository().addMatch("#", roles);
+      ClientSessionFactory cf = createSessionFactory(locator);
+
+      try {
+         ClientSession session = cf.createSession("first", "secret", false, true, true, false, 0);
+         server.createQueue(new QueueConfiguration("queue").setAddress("address").setRoutingType(RoutingType.ANYCAST));
+         ClientProducer producer = session.createProducer("address");
+         producer.send(session.createMessage(true));
+         session.commit();
+         producer.close();
+         ClientConsumer consumer = session.createConsumer("queue");
+         session.start();
+         ClientMessage message = consumer.receive(1000);
+         assertNotNull(message);
+         assertEquals("first", message.getValidatedUserID());
+         session.close();
+      } catch (ActiveMQException e) {
+         e.printStackTrace();
+         Assert.fail("should not throw exception");
+      }
+   }
+
+   @Test
+   public void testAuthenticationBadPassword() throws Exception {
+      ActiveMQServer server = initializeServer();
+      server.start();
+      ClientSessionFactory cf = createSessionFactory(locator);
+
+      try {
+         cf.createSession("first", "badpassword", false, true, true, false, 0);
+         Assert.fail("should throw exception here");
+      } catch (Exception e) {
+         // ignore
+      }
+   }
+
+   @Test
+   public void testAuthorizationNegative() throws Exception {
+      final SimpleString ADDRESS = new SimpleString("address");
+      final SimpleString DURABLE_QUEUE = new SimpleString("durableQueue");
+      final SimpleString NON_DURABLE_QUEUE = new SimpleString("nonDurableQueue");
+
+      ActiveMQServer server = initializeServer();
+      Set<Role> roles = new HashSet<>();
+      roles.add(new Role("programmers", false, false, false, false, false, false, false, false, false, false));
+      server.getConfiguration().putSecurityRoles("#", roles);
+      server.start();
+      server.addAddressInfo(new AddressInfo(ADDRESS, RoutingType.ANYCAST));
+      server.createQueue(new QueueConfiguration(DURABLE_QUEUE).setAddress(ADDRESS).setRoutingType(RoutingType.ANYCAST));
+      server.createQueue(new QueueConfiguration(NON_DURABLE_QUEUE).setAddress(ADDRESS).setRoutingType(RoutingType.ANYCAST).setDurable(false));
+
+      ClientSessionFactory cf = createSessionFactory(locator);
+      ClientSession session = addClientSession(cf.createSession("first", "secret", false, true, true, false, 0));
+
+      // CREATE_DURABLE_QUEUE
+      try {
+         session.createQueue(new QueueConfiguration(DURABLE_QUEUE).setAddress(ADDRESS));
+         Assert.fail("should throw exception here");
+      } catch (ActiveMQException e) {
+         assertTrue(e.getMessage().contains("User: first does not have permission='CREATE_DURABLE_QUEUE' for queue durableQueue on address address"));
+      }
+
+      // DELETE_DURABLE_QUEUE
+      try {
+         session.deleteQueue(DURABLE_QUEUE);
+         Assert.fail("should throw exception here");
+      } catch (ActiveMQException e) {
+         assertTrue(e.getMessage().contains("User: first does not have permission='DELETE_DURABLE_QUEUE' for queue durableQueue on address address"));
+      }
+
+      // CREATE_NON_DURABLE_QUEUE
+      try {
+         session.createQueue(new QueueConfiguration(NON_DURABLE_QUEUE).setAddress(ADDRESS).setDurable(false));
+         Assert.fail("should throw exception here");
+      } catch (ActiveMQException e) {
+         assertTrue(e.getMessage().contains("User: first does not have permission='CREATE_NON_DURABLE_QUEUE' for queue nonDurableQueue on address address"));
+      }
+
+      // DELETE_NON_DURABLE_QUEUE
+      try {
+         session.deleteQueue(NON_DURABLE_QUEUE);
+         Assert.fail("should throw exception here");
+      } catch (ActiveMQException e) {
+         assertTrue(e.getMessage().contains("User: first does not have permission='DELETE_NON_DURABLE_QUEUE' for queue nonDurableQueue on address address"));
+      }
+
+      // PRODUCE
+      try {
+         ClientProducer producer = session.createProducer(ADDRESS);
+         producer.send(session.createMessage(true));
+         Assert.fail("should throw exception here");
+      } catch (ActiveMQException e) {
+         assertTrue(e.getMessage().contains("User: first does not have permission='SEND' on address address"));
+      }
+
+      // CONSUME
+      try {
+         ClientConsumer consumer = session.createConsumer(DURABLE_QUEUE);
+         Assert.fail("should throw exception here");
+      } catch (ActiveMQException e) {
+         assertTrue(e.getMessage().contains("User: first does not have permission='CONSUME' for queue durableQueue on address address"));
+      }
+
+      // MANAGE
+      try {
+         ClientProducer producer = session.createProducer(server.getConfiguration().getManagementAddress());
+         producer.send(session.createMessage(true));
+         Assert.fail("should throw exception here");
+      } catch (ActiveMQException e) {
+         assertTrue(e.getMessage().contains("User: first does not have permission='MANAGE' on address activemq.management"));
+      }
+
+      // BROWSE
+      try {
+         ClientConsumer browser = session.createConsumer(DURABLE_QUEUE, true);
+         Assert.fail("should throw exception here");
+      } catch (ActiveMQException e) {
+         assertTrue(e.getMessage().contains("User: first does not have permission='BROWSE' for queue durableQueue on address address"));
+      }
+   }
+
+   @Test
+   public void testAuthorizationPositive() throws Exception {
+      final SimpleString ADDRESS = new SimpleString("address");
+      final SimpleString DURABLE_QUEUE = new SimpleString("durableQueue");
+      final SimpleString NON_DURABLE_QUEUE = new SimpleString("nonDurableQueue");
+
+      ActiveMQServer server = initializeServer();
+      Set<Role> roles = new HashSet<>();
+      roles.add(new Role("programmers", true, true, true, true, true, true, true, true, true, true));
+      server.getConfiguration().putSecurityRoles("#", roles);
+      server.start();
+
+      ClientSessionFactory cf = createSessionFactory(locator);
+      ClientSession session = addClientSession(cf.createSession("first", "secret", false, true, true, false, 0));
+
+      // CREATE_DURABLE_QUEUE
+      try {
+         session.createQueue(new QueueConfiguration(DURABLE_QUEUE).setAddress(ADDRESS));
+      } catch (ActiveMQException e) {
+         Assert.fail("should not throw exception here");
+      }
+
+      // DELETE_DURABLE_QUEUE
+      try {
+         session.deleteQueue(DURABLE_QUEUE);
+      } catch (ActiveMQException e) {
+         Assert.fail("should not throw exception here");
+      }
+
+      // CREATE_NON_DURABLE_QUEUE
+      try {
+         session.createQueue(new QueueConfiguration(NON_DURABLE_QUEUE).setAddress(ADDRESS).setDurable(false));
+      } catch (ActiveMQException e) {
+         Assert.fail("should not throw exception here");
+      }
+
+      // DELETE_NON_DURABLE_QUEUE
+      try {
+         session.deleteQueue(NON_DURABLE_QUEUE);
+      } catch (ActiveMQException e) {
+         Assert.fail("should not throw exception here");
+      }
+
+      session.createQueue(new QueueConfiguration(DURABLE_QUEUE).setAddress(ADDRESS));
+
+      // PRODUCE
+      try {
+         ClientProducer producer = session.createProducer(ADDRESS);
+         producer.send(session.createMessage(true));
+      } catch (ActiveMQException e) {
+         Assert.fail("should not throw exception here");
+      }
+
+      // CONSUME
+      try {
+         session.createConsumer(DURABLE_QUEUE);
+      } catch (ActiveMQException e) {
+         Assert.fail("should not throw exception here");
+      }
+
+      // MANAGE
+      try {
+         ClientProducer producer = session.createProducer(server.getConfiguration().getManagementAddress());
+         producer.send(session.createMessage(true));
+      } catch (ActiveMQException e) {
+         Assert.fail("should not throw exception here");
+      }
+
+      // BROWSE
+      try {
+         session.createConsumer(DURABLE_QUEUE, true);
+      } catch (ActiveMQException e) {
+         Assert.fail("should not throw exception here");
+      }
+   }
+}