You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by cl...@apache.org on 2019/02/07 15:16:13 UTC
[activemq-artemis] branch master updated: ARTEMIS-2243 user/role
ops for PropertiesLoginModule via mgmnt
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 4a1fc61 ARTEMIS-2243 user/role ops for PropertiesLoginModule via mgmnt
new 9b9fe8a This closes #2532
4a1fc61 is described below
commit 4a1fc61fcc8c5a9404f15d3bae845fe33219e48a
Author: Justin Bertram <jb...@apache.org>
AuthorDate: Tue Jan 29 18:35:16 2019 -0600
ARTEMIS-2243 user/role ops for PropertiesLoginModule via mgmnt
---
artemis-cli/pom.xml | 7 +-
.../artemis/cli/commands/user/AddUser.java | 3 +-
.../artemis/cli/commands/user/ListUser.java | 25 +-
.../artemis/cli/commands/user/RemoveUser.java | 3 +-
.../artemis/cli/commands/user/ResetUser.java | 3 +-
.../artemis/cli/commands/user/UserAction.java | 32 ---
.../org/apache/activemq/cli/test/ArtemisTest.java | 276 ++++++++++++++++++++-
.../api/core/management/ActiveMQServerControl.java | 54 ++++
.../src/main/resources/bin/artemis | 2 +-
.../src/main/resources/bin/artemis.cmd | 2 +-
artemis-features/src/main/resources/features.xml | 3 +
artemis-server/pom.xml | 4 +
.../management/impl/ActiveMQServerControlImpl.java | 50 ++++
.../artemis/core/server/ActiveMQMessageBundle.java | 20 ++
.../jaas/PropertiesLoginModuleConfigurator.java | 147 ++++++-----
docs/user-manual/en/security.md | 11 +
pom.xml | 2 +-
.../management/ActiveMQServerControlTest.java | 40 +++
.../ActiveMQServerControlUsingCoreTest.java | 25 ++
19 files changed, 602 insertions(+), 107 deletions(-)
diff --git a/artemis-cli/pom.xml b/artemis-cli/pom.xml
index d0007bc..3d53545 100644
--- a/artemis-cli/pom.xml
+++ b/artemis-cli/pom.xml
@@ -118,13 +118,12 @@
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
- <artifactId>commons-configuration2</artifactId>
- <version>${commons.config.version}</version>
+ <artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- <version>${commons.lang.version}</version>
+ <artifactId>commons-configuration2</artifactId>
+ <scope>test</scope>
</dependency>
<dependency>
<groupId>com.sun.winsw</groupId>
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/AddUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/AddUser.java
index 37bd676..a105bcd 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/AddUser.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/AddUser.java
@@ -20,6 +20,7 @@ import io.airlift.airline.Command;
import io.airlift.airline.Option;
import org.apache.activemq.artemis.cli.commands.ActionContext;
import org.apache.activemq.artemis.cli.commands.util.HashUtil;
+import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModuleConfigurator;
import org.apache.commons.lang3.StringUtils;
/**
@@ -53,7 +54,7 @@ public class AddUser extends PasswordAction {
* @throws IllegalArgumentException if user exists
*/
private void add(String hash, String... role) throws Exception {
- FileBasedSecStoreConfig config = getConfiguration();
+ PropertiesLoginModuleConfigurator config = new PropertiesLoginModuleConfigurator(entry, getBrokerEtc());
config.addNewUser(username, hash, role);
config.save();
context.out.println("User added successfully.");
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ListUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ListUser.java
index c0fb979..b18deba 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ListUser.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ListUser.java
@@ -16,10 +16,12 @@
*/
package org.apache.activemq.artemis.cli.commands.user;
-import java.util.List;
+import java.util.Map;
+import java.util.Set;
import io.airlift.airline.Command;
import org.apache.activemq.artemis.cli.commands.ActionContext;
+import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModuleConfigurator;
/**
* list existing users, example:
@@ -42,11 +44,24 @@ public class ListUser extends UserAction {
* if username is not specified
*/
private void list() throws Exception {
- FileBasedSecStoreConfig config = getConfiguration();
- List<String> result = config.listUser(username);
- for (String str : result) {
- context.out.println(str);
+ PropertiesLoginModuleConfigurator config = new PropertiesLoginModuleConfigurator(entry, getBrokerEtc());
+ Map<String, Set<String>> result = config.listUser(username);
+ StringBuilder logMessage = new StringBuilder("--- \"user\"(roles) ---\n");
+ int userCount = 0;
+ for (Map.Entry<String, Set<String>> entry : result.entrySet()) {
+ logMessage.append("\"").append(entry.getKey()).append("\"(");
+ int roleCount = 0;
+ for (String role : entry.getValue()) {
+ logMessage.append(role);
+ if (++roleCount < entry.getValue().size()) {
+ logMessage.append(",");
+ }
+ }
+ logMessage.append(")\n");
+ userCount++;
}
+ logMessage.append("\n Total: ").append(userCount);
+ context.out.println(logMessage);
}
}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/RemoveUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/RemoveUser.java
index a9dce8d..c49e36b 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/RemoveUser.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/RemoveUser.java
@@ -18,6 +18,7 @@ package org.apache.activemq.artemis.cli.commands.user;
import io.airlift.airline.Command;
import org.apache.activemq.artemis.cli.commands.ActionContext;
+import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModuleConfigurator;
/**
* Remove a user, example:
@@ -35,7 +36,7 @@ public class RemoveUser extends UserAction {
}
private void remove() throws Exception {
- FileBasedSecStoreConfig config = getConfiguration();
+ PropertiesLoginModuleConfigurator config = new PropertiesLoginModuleConfigurator(entry, getBrokerEtc());
config.removeUser(username);
config.save();
context.out.println("User removed.");
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ResetUser.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ResetUser.java
index 2e3e725..162aea6 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ResetUser.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/ResetUser.java
@@ -20,6 +20,7 @@ import io.airlift.airline.Command;
import io.airlift.airline.Option;
import org.apache.activemq.artemis.cli.commands.ActionContext;
import org.apache.activemq.artemis.cli.commands.util.HashUtil;
+import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModuleConfigurator;
import org.apache.commons.lang3.StringUtils;
/**
@@ -57,7 +58,7 @@ public class ResetUser extends PasswordAction {
context.err.println("Nothing to update.");
return;
}
- FileBasedSecStoreConfig config = getConfiguration();
+ PropertiesLoginModuleConfigurator config = new PropertiesLoginModuleConfigurator(entry, getBrokerEtc());
config.updateUser(username, password, roles);
config.save();
context.out.println("User updated");
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserAction.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserAction.java
index e4a2e45..d6f8ba9 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserAction.java
+++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/UserAction.java
@@ -16,16 +16,8 @@
*/
package org.apache.activemq.artemis.cli.commands.user;
-import javax.security.auth.login.AppConfigurationEntry;
-import javax.security.auth.login.Configuration;
-import java.io.File;
-
import io.airlift.airline.Option;
import org.apache.activemq.artemis.cli.commands.InputAbstract;
-import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule;
-
-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 abstract class UserAction extends InputAbstract {
@@ -50,30 +42,6 @@ public abstract class UserAction extends InputAbstract {
}
}
- FileBasedSecStoreConfig getConfiguration() throws Exception {
-
- Configuration securityConfig = Configuration.getConfiguration();
- AppConfigurationEntry[] entries = securityConfig.getAppConfigurationEntry(entry);
-
- for (AppConfigurationEntry entry : entries) {
- if (entry.getLoginModuleName().equals(PropertiesLoginModule.class.getName())) {
- String userFileName = (String) entry.getOptions().get(USER_FILE_PROP_NAME);
- String roleFileName = (String) entry.getOptions().get(ROLE_FILE_PROP_NAME);
-
- File etcDir = new File(getBrokerEtc());
- File userFile = new File(etcDir, userFileName);
- File roleFile = new File(etcDir, roleFileName);
-
- if (!userFile.exists() || !roleFile.exists()) {
- throw new IllegalArgumentException("Couldn't find user file or role file!");
- }
-
- return new FileBasedSecStoreConfig(userFile, roleFile);
- }
- }
- throw new IllegalArgumentException("Failed to load security file");
- }
-
public void setUsername(String username) {
this.username = username;
}
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 23493b9..cb89809 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
@@ -23,6 +23,8 @@ import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@@ -34,20 +36,24 @@ import java.io.InputStreamReader;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Pattern;
+import org.apache.activemq.artemis.api.core.ActiveMQIllegalStateException;
+import org.apache.activemq.artemis.api.core.JsonUtil;
import org.apache.activemq.artemis.api.core.Pair;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
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.api.core.management.ActiveMQServerControl;
import org.apache.activemq.artemis.cli.Artemis;
import org.apache.activemq.artemis.cli.CLIException;
import org.apache.activemq.artemis.cli.commands.ActionContext;
import org.apache.activemq.artemis.cli.commands.Create;
import org.apache.activemq.artemis.cli.commands.Mask;
-import org.apache.activemq.artemis.cli.commands.queue.StatQueue;
import org.apache.activemq.artemis.cli.commands.Run;
+import org.apache.activemq.artemis.cli.commands.queue.StatQueue;
import org.apache.activemq.artemis.cli.commands.user.AddUser;
import org.apache.activemq.artemis.cli.commands.user.ListUser;
import org.apache.activemq.artemis.cli.commands.user.RemoveUser;
@@ -390,6 +396,173 @@ public class ArtemisTest extends CliTestBase {
}
@Test
+ public void testUserCommandViaManagement() 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");
+ System.setProperty("artemis.instance", instance1.getAbsolutePath());
+ Object result = Artemis.internalExecute("run");
+ ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
+ ActiveMQServerControl activeMQServerControl = activeMQServer.getActiveMQServerControl();
+
+ File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
+ File roleFile = new File(instance1.getAbsolutePath() + "/etc/artemis-roles.properties");
+
+ //default only one user admin with role amq
+ String jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
+ checkRole("admin", roleFile, "amq");
+
+ //add a simple user
+ activeMQServerControl.addUser("guest", "guest123", "admin", true, "activemq");
+
+ //verify add
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin");
+ checkRole("guest", roleFile, "admin");
+ assertTrue(checkPassword("guest", "guest123", userFile));
+
+ //add a user with 2 roles
+ activeMQServerControl.addUser("scott", "tiger", "admin,operator", true, "activemq");
+
+ //verify add
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin");
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator");
+ checkRole("scott", roleFile, "admin", "operator");
+ assertTrue(checkPassword("scott", "tiger", userFile));
+
+ try {
+ activeMQServerControl.addUser("scott", "password", "visitor", true, "activemq");
+ fail("should throw an exception if adding a existing user");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ //check existing users are intact
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
+ contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin");
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin");
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator");
+
+ //check listing with just one user
+ jsonResult = activeMQServerControl.listUser("admin", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
+ contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin", false);
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin", false);
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator", false);
+
+ //check listing with another single user
+ jsonResult = activeMQServerControl.listUser("guest", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq", false);
+ contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin");
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin", false);
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator", false);
+
+ //remove a user
+ activeMQServerControl.removeUser("guest", "activemq");
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
+ contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin", false);
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin");
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator");
+
+ //remove another
+ activeMQServerControl.removeUser("scott", "activemq");
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
+ contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin", false);
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin", false);
+ contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator", false);
+
+ //remove non-exist
+ try {
+ activeMQServerControl.removeUser("alien", "activemq");
+ fail("should throw exception when removing a non-existing user");
+ } catch (IllegalArgumentException expected) {
+ }
+
+ //check
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
+
+ //now remove last
+ activeMQServerControl.removeUser("admin", "activemq");
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq", false);
+
+ stopServer();
+ }
+
+ @Test
+ public void testBadSecurityEntryNameViaManagement() 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");
+ System.setProperty("artemis.instance", instance1.getAbsolutePath());
+ Object result = Artemis.internalExecute("run");
+ ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
+ ActiveMQServerControl activeMQServerControl = activeMQServer.getActiveMQServerControl();
+
+ try {
+ activeMQServerControl.listUser("", "activemqx");
+ fail();
+ } catch (ActiveMQIllegalStateException expected) {
+ }
+
+ stopServer();
+ }
+
+ @Test
+ public void testMissingUserFileViaManagement() 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");
+ System.setProperty("artemis.instance", instance1.getAbsolutePath());
+ Object result = Artemis.internalExecute("run");
+ ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
+ ActiveMQServerControl activeMQServerControl = activeMQServer.getActiveMQServerControl();
+
+ File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
+ userFile.delete();
+ // File roleFile = new File(instance1.getAbsolutePath() + "/etc/artemis-roles.properties");
+
+ try {
+ activeMQServerControl.listUser("", "activemq");
+ fail();
+ } catch (ActiveMQIllegalStateException expected) {
+ }
+
+ stopServer();
+ }
+
+ @Test
+ public void testMissingRoleFileViaManagement() 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");
+ System.setProperty("artemis.instance", instance1.getAbsolutePath());
+ Object result = Artemis.internalExecute("run");
+ ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
+ ActiveMQServerControl activeMQServerControl = activeMQServer.getActiveMQServerControl();
+
+ File roleFile = new File(instance1.getAbsolutePath() + "/etc/artemis-roles.properties");
+ roleFile.delete();
+
+ try {
+ activeMQServerControl.listUser("", "activemq");
+ fail();
+ } catch (ActiveMQIllegalStateException expected) {
+ }
+
+ stopServer();
+ }
+
+ @Test
public void testUserCommandReset() throws Exception {
Run.setEmbedded(true);
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
@@ -454,9 +627,9 @@ public class ArtemisTest extends CliTestBase {
assertTrue(result.contains("Total: 4"));
assertTrue(result.contains("\"guest\"(admin)"));
- assertTrue(result.contains("\"user1\"(admin,manager)"));
- assertTrue(result.contains("\"user2\"(admin,manager,master)"));
- assertTrue(result.contains("\"user3\"(master,system)"));
+ assertTrue(Pattern.compile("\"user1\"\\((admin|manager),(admin|manager)\\)").matcher(result).find());
+ assertTrue(Pattern.compile("\"user2\"\\((admin|manager|master),(admin|manager|master),(admin|manager|master)\\)").matcher(result).find());
+ assertTrue(Pattern.compile("\"user3\"\\((master|system),(master|system)\\)").matcher(result).find());
checkRole("user1", roleFile, "admin", "manager");
@@ -489,6 +662,72 @@ public class ArtemisTest extends CliTestBase {
}
@Test
+ public void testUserCommandResetViaManagement() 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");
+ System.setProperty("artemis.instance", instance1.getAbsolutePath());
+ Object result = Artemis.internalExecute("run");
+ ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
+ ActiveMQServerControl activeMQServerControl = activeMQServer.getActiveMQServerControl();
+
+ File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
+ File roleFile = new File(instance1.getAbsolutePath() + "/etc/artemis-roles.properties");
+
+ //default only one user admin with role amq
+ String jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
+ checkRole("admin", roleFile, "amq");
+
+ //remove a user
+ activeMQServerControl.removeUser("admin", "activemq");
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq", false);
+
+ //add some users
+ activeMQServerControl.addUser("guest", "guest123", "admin", true, "activemq");
+ activeMQServerControl.addUser("user1", "password1", "admin,manager", true, "activemq");
+ assertTrue(checkPassword("user1", "password1", userFile));
+ activeMQServerControl.addUser("user2", "password2", "admin,manager,master", true, "activemq");
+ activeMQServerControl.addUser("user3", "password3", "system,master", true, "activemq");
+
+
+ //verify use list cmd
+ jsonResult = activeMQServerControl.listUser("", "activemq");
+ contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin");
+ contains(JsonUtil.readJsonArray(jsonResult), "user1", "admin");
+ contains(JsonUtil.readJsonArray(jsonResult), "user1", "manager");
+ contains(JsonUtil.readJsonArray(jsonResult), "user2", "admin");
+ contains(JsonUtil.readJsonArray(jsonResult), "user2", "manager");
+ contains(JsonUtil.readJsonArray(jsonResult), "user2", "master");
+ contains(JsonUtil.readJsonArray(jsonResult), "user3", "master");
+ contains(JsonUtil.readJsonArray(jsonResult), "user3", "system");
+
+ checkRole("user1", roleFile, "admin", "manager");
+
+ //reset password
+ activeMQServerControl.resetUser("user1", "newpassword1", null, "activemq");
+
+ checkRole("user1", roleFile, "admin", "manager");
+ assertFalse(checkPassword("user1", "password1", userFile));
+ assertTrue(checkPassword("user1", "newpassword1", userFile));
+
+ //reset role
+ activeMQServerControl.resetUser("user2", null, "manager,master,operator", "activemq");
+
+ checkRole("user2", roleFile, "manager", "master", "operator");
+ assertTrue(checkPassword("user2", "password2", userFile));
+
+ //reset both
+ activeMQServerControl.resetUser("user3", "newpassword3", "admin,system", "activemq");
+
+ checkRole("user3", roleFile, "admin", "system");
+ assertTrue(checkPassword("user3", "newpassword3", userFile));
+ stopServer();
+ }
+
+ @Test
public void testMaskCommand() throws Exception {
String password1 = "password";
@@ -1074,4 +1313,33 @@ public class ArtemisTest extends CliTestBase {
return processor.compare(password.toCharArray(), storedPassword);
}
+ private void contains(JsonArray users, String username, String role) {
+ contains(users, username, role, true);
+ }
+
+ private void contains(JsonArray users, String username, String role, boolean contains) {
+ boolean userFound = false;
+ boolean roleFound = false;
+ for (int i = 0; i < users.size(); i++) {
+ JsonObject user = users.getJsonObject(i);
+ if (user.getString("username").equals(username)) {
+ userFound = true;
+ JsonArray roles = user.getJsonArray("roles");
+ for (int j = 0; j < roles.size(); j++) {
+ if (roles.getString(j).equals(role)) {
+ roleFound = true;
+ break;
+ }
+ }
+ }
+ }
+ if (contains) {
+ assertTrue("user " + username + " not found", userFound);
+ assertTrue("role " + role + " not found", roleFound);
+ } else {
+ assertFalse("user " + username + " found", userFound);
+ assertFalse("role " + role + " found", roleFound);
+ }
+ }
+
}
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 06819b2..04c7e16 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
@@ -1341,5 +1341,59 @@ public interface ActiveMQServerControl {
*/
@Operation(desc = "Names of the cluster-connections deployed on this server", impact = MBeanOperationInfo.INFO)
String[] getClusterConnectionNames();
+
+ /**
+ * Add a user (only applicable when using the JAAS PropertiesLoginModule)
+ *
+ * @param username
+ * @param password
+ * @param roles
+ * @param entryName
+ * @throws Exception
+ */
+ @Operation(desc = "add a user (only applicable when using the JAAS PropertiesLoginModule)", 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 (comma separated)", desc = "User's role") String roles,
+ @Parameter(name = "plaintext", desc = "whether or not to store the password in plaintext or hash it") boolean plaintext,
+ @Parameter(name = "entryName", desc = "Name of entry in login.config ('activemq' by default)") String entryName) throws Exception;
+
+ /**
+ * List the information about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule).
+ *
+ * @param username
+ * @param entryName
+ * @return JSON array of user & 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)
+ String listUser(@Parameter(name = "username", desc = "Name of the user; leave null to list all known users") String username,
+ @Parameter(name = "entryName", desc = "Name of entry in login.config ('activemq' by default)") String entryName) throws Exception;
+
+ /**
+ * Remove a user (only applicable when using the JAAS PropertiesLoginModule).
+ *
+ * @param username
+ * @param entryName
+ * @throws Exception
+ */
+ @Operation(desc = "remove a user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
+ void removeUser(@Parameter(name = "username", desc = "Name of the user") String username,
+ @Parameter(name = "entryName", desc = "Name of entry in login.config ('activemq' by default)") String entryName) throws Exception;
+
+ /**
+ * Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule).
+ *
+ * @param username
+ * @param password
+ * @param roles
+ * @param entryName
+ * @throws Exception
+ */
+ @Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule)", 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 (comma separated)", desc = "User's role") String roles,
+ @Parameter(name = "entryName", desc = "Name of entry in login.config ('activemq' by default)") String entryName) throws Exception;
}
diff --git a/artemis-distribution/src/main/resources/bin/artemis b/artemis-distribution/src/main/resources/bin/artemis
index 4b5ebe5..6fb7385 100755
--- a/artemis-distribution/src/main/resources/bin/artemis
+++ b/artemis-distribution/src/main/resources/bin/artemis
@@ -46,7 +46,7 @@ fi
# Set Defaults Properties
JAVA_ARGS="-XX:+UseParallelGC -Xms512M -Xmx1024M"
-CLASSPATH="$ARTEMIS_HOME/lib/artemis-boot.jar"
+CLASSPATH="$ARTEMIS_HOME/lib/*"
# OS specific support.
cygwin=false;
diff --git a/artemis-distribution/src/main/resources/bin/artemis.cmd b/artemis-distribution/src/main/resources/bin/artemis.cmd
index 66f4cdb..c43d1b8 100755
--- a/artemis-distribution/src/main/resources/bin/artemis.cmd
+++ b/artemis-distribution/src/main/resources/bin/artemis.cmd
@@ -50,7 +50,7 @@ set JAVA_ARGS=-XX:+UseParallelGC -Xms512M -Xmx1024M
rem "Create full JVM Args"
set JVM_ARGS=%JAVA_ARGS%
if not "%ARTEMIS_CLUSTER_PROPS%"=="" set JVM_ARGS=%JVM_ARGS% %ARTEMIS_CLUSTER_PROPS%
-set JVM_ARGS=%JVM_ARGS% -classpath %ARTEMIS_HOME%\lib\artemis-boot.jar
+set JVM_ARGS=%JVM_ARGS% -classpath %ARTEMIS_HOME%\lib\*
set JVM_ARGS=%JVM_ARGS% -Dartemis.home=%ARTEMIS_HOME%
if not "%DEBUG_ARGS%"=="" set JVM_ARGS=%JVM_ARGS% %DEBUG_ARGS%
diff --git a/artemis-features/src/main/resources/features.xml b/artemis-features/src/main/resources/features.xml
index 38764e9..d3bcae4 100644
--- a/artemis-features/src/main/resources/features.xml
+++ b/artemis-features/src/main/resources/features.xml
@@ -62,6 +62,9 @@
<configfile finalname="etc/artemis.xml">mvn:org.apache.activemq/artemis-features/${pom.version}/xml/artemis</configfile>
<bundle dependency="true">mvn:org.apache.geronimo.specs/geronimo-jms_2.0_spec/${geronimo.jms.2.spec.version}</bundle>
+ <bundle dependency="true">mvn:org.apache.commons/commons-configuration2/${commons.config.version}</bundle>
+ <bundle dependency="true">mvn:org.apache.commons/commons-text/1.6</bundle>
+ <bundle dependency="true">mvn:org.apache.commons/commons-lang3/${commons.lang.version}</bundle>
<bundle>mvn:org.apache.activemq/artemis-native/${pom.version}</bundle>
<bundle>mvn:org.apache.activemq/artemis-server-osgi/${pom.version}</bundle>
diff --git a/artemis-server/pom.xml b/artemis-server/pom.xml
index f24731b..d7b8f70 100644
--- a/artemis-server/pom.xml
+++ b/artemis-server/pom.xml
@@ -128,6 +128,10 @@
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-configuration2</artifactId>
+ </dependency>
+ <dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
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 302da89..c89dacb 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
@@ -30,6 +30,7 @@ import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.transaction.xa.Xid;
+import java.net.URL;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -118,8 +119,10 @@ import org.apache.activemq.artemis.core.transaction.TransactionDetailFactory;
import org.apache.activemq.artemis.core.transaction.impl.CoreTransactionDetail;
import org.apache.activemq.artemis.core.transaction.impl.XidImpl;
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
+import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModuleConfigurator;
import org.apache.activemq.artemis.utils.JsonLoader;
import org.apache.activemq.artemis.utils.ListUtil;
+import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
import org.apache.activemq.artemis.utils.SecurityFormatter;
import org.apache.activemq.artemis.utils.collections.TypedProperties;
import org.jboss.logging.Logger;
@@ -2956,5 +2959,52 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
this.broadcaster.sendNotification(new Notification(type.toString(), this, notifSeq.incrementAndGet(), notification.toString()));
}
+ @Override
+ public void addUser(String username, String password, String roles, boolean plaintext, String entryName) throws Exception {
+ PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator(entryName);
+ config.addNewUser(username, plaintext ? password : PasswordMaskingUtil.getHashProcessor().hash(password), roles.split(","));
+ config.save();
+ }
+
+ @Override
+ public String listUser(String username, String entryName) throws Exception {
+ PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator(entryName);
+ 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();
+ }
+
+ @Override
+ public void removeUser(String username, String entryName) throws Exception {
+ PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator(entryName);
+ config.removeUser(username);
+ config.save();
+ }
+
+ @Override
+ public void resetUser(String username, String password, String roles, String entryName) throws Exception {
+ PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator(entryName);
+ config.updateUser(username, password, roles == null ? null : roles.split(","));
+ config.save();
+ }
+
+ private PropertiesLoginModuleConfigurator getPropertiesLoginModuleConfigurator(String entryName) throws Exception {
+ URL configurationUrl = server.getConfiguration().getConfigurationUrl();
+ if (configurationUrl == null) {
+ throw ActiveMQMessageBundle.BUNDLE.failedToLocateConfigURL();
+ }
+ String path = configurationUrl.getPath();
+ return new PropertiesLoginModuleConfigurator(entryName, path.substring(0, path.lastIndexOf("/")));
+ }
}
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 d649d8d..5cb8c60 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
@@ -449,4 +449,24 @@ public interface ActiveMQMessageBundle {
@Message(id = 119217, value = "Can't write to closed file: {0}", format = Message.Format.MESSAGE_FORMAT)
ActiveMQIOErrorException cannotWriteToClosedFile(SequentialFile file);
+ @Message(id = 229218, value = "Failed to locate broker configuration URL")
+ ActiveMQIllegalStateException failedToLocateConfigURL();
+
+ @Message(id = 229219, value = "Failed to load security configuration")
+ ActiveMQIllegalStateException failedToLoadSecurityConfig();
+
+ @Message(id = 229220, value = "Failed to load user file: {0}", format = Message.Format.MESSAGE_FORMAT)
+ ActiveMQIllegalStateException failedToLoadUserFile(String path);
+
+ @Message(id = 229221, value = "Failed to load role file: {0}", format = Message.Format.MESSAGE_FORMAT)
+ ActiveMQIllegalStateException failedToLoadRoleFile(String path);
+
+ @Message(id = 229222, value = "Failed to find login module entry {0} from JAAS configuration", format = Message.Format.MESSAGE_FORMAT)
+ ActiveMQIllegalStateException failedToFindLoginModuleEntry(String entry);
+
+ @Message(id = 229223, value = "User {0} already exists", format = Message.Format.MESSAGE_FORMAT)
+ IllegalArgumentException userAlreadyExists(String user);
+
+ @Message(id = 229224, value = "User {0} does not exist", format = Message.Format.MESSAGE_FORMAT)
+ IllegalArgumentException userDoesNotExist(String user);
}
diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/FileBasedSecStoreConfig.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModuleConfigurator.java
similarity index 57%
rename from artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/FileBasedSecStoreConfig.java
rename to artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModuleConfigurator.java
index 1f8e297..d1e7873 100644
--- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/user/FileBasedSecStoreConfig.java
+++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/jaas/PropertiesLoginModuleConfigurator.java
@@ -14,20 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.activemq.artemis.cli.commands.user;
+package org.apache.activemq.artemis.spi.core.security.jaas;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
import java.io.File;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+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.utils.StringUtil;
import org.apache.commons.configuration2.PropertiesConfiguration;
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
import org.apache.commons.configuration2.builder.fluent.Configurations;
-class FileBasedSecStoreConfig {
+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 {
private static final String LICENSE_HEADER =
"## ---------------------------------------------------------------------------\n" +
@@ -51,80 +61,110 @@ class FileBasedSecStoreConfig {
private PropertiesConfiguration userConfig;
private PropertiesConfiguration roleConfig;
- FileBasedSecStoreConfig(File userFile, File roleFile) throws Exception {
- Configurations configs = new Configurations();
- userBuilder = configs.propertiesBuilder(userFile);
- roleBuilder = configs.propertiesBuilder(roleFile);
- userConfig = userBuilder.getConfiguration();
- roleConfig = roleBuilder.getConfiguration();
-
- String roleHeader = roleConfig.getLayout().getHeaderComment();
- String userHeader = userConfig.getLayout().getHeaderComment();
-
- if (userHeader == null) {
- if (userConfig.isEmpty()) {
- //clean and reset header
- userConfig.clear();
- userConfig.setHeader(LICENSE_HEADER);
- }
+ public PropertiesLoginModuleConfigurator(String entryName, String brokerEtc) throws Exception {
+ if (entryName == null || entryName.length() == 0) {
+ entryName = "activemq";
+ }
+
+ Configuration securityConfig = Configuration.getConfiguration();
+ AppConfigurationEntry[] entries = securityConfig.getAppConfigurationEntry(entryName);
+
+ if (entries == null || entries.length == 0) {
+ throw ActiveMQMessageBundle.BUNDLE.failedToLoadSecurityConfig();
}
- if (roleHeader == null) {
- if (roleConfig.isEmpty()) {
- //clean and reset header
- roleConfig.clear();
- roleConfig.setHeader(LICENSE_HEADER);
+ int entriesInspected = 0;
+ for (AppConfigurationEntry entry : entries) {
+ entriesInspected++;
+ if (entry.getLoginModuleName().equals(PropertiesLoginModule.class.getName())) {
+ String userFileName = (String) entry.getOptions().get(USER_FILE_PROP_NAME);
+ String roleFileName = (String) entry.getOptions().get(ROLE_FILE_PROP_NAME);
+
+ File etcDir = new File(brokerEtc);
+ File userFile = new File(etcDir, userFileName);
+ File roleFile = new File(etcDir, roleFileName);
+
+ if (!userFile.exists()) {
+ throw ActiveMQMessageBundle.BUNDLE.failedToLoadUserFile(brokerEtc + userFileName);
+ }
+
+ if (!roleFile.exists()) {
+ throw ActiveMQMessageBundle.BUNDLE.failedToLoadRoleFile(brokerEtc + roleFileName);
+ }
+
+ Configurations configs = new Configurations();
+ userBuilder = configs.propertiesBuilder(userFile);
+ roleBuilder = configs.propertiesBuilder(roleFile);
+ userConfig = userBuilder.getConfiguration();
+ roleConfig = roleBuilder.getConfiguration();
+
+ String roleHeader = roleConfig.getLayout().getHeaderComment();
+ String userHeader = userConfig.getLayout().getHeaderComment();
+
+ if (userHeader == null) {
+ if (userConfig.isEmpty()) {
+ //clean and reset header
+ userConfig.clear();
+ userConfig.setHeader(LICENSE_HEADER);
+ }
+ }
+
+ if (roleHeader == null) {
+ if (roleConfig.isEmpty()) {
+ //clean and reset header
+ roleConfig.clear();
+ roleConfig.setHeader(LICENSE_HEADER);
+ }
+ }
+ return;
}
}
+
+ if (entriesInspected == entries.length) {
+ throw ActiveMQMessageBundle.BUNDLE.failedToFindLoginModuleEntry(entryName);
+ }
}
- void addNewUser(String username, String hash, String... roles) throws Exception {
+ public void addNewUser(String username, String hash, String... roles) throws Exception {
if (userConfig.getString(username) != null) {
- throw new IllegalArgumentException("User already exist: " + username);
+ throw ActiveMQMessageBundle.BUNDLE.userAlreadyExists(username);
}
userConfig.addProperty(username, hash);
addRoles(username, roles);
}
- void save() throws Exception {
+ public void save() throws Exception {
userBuilder.save();
roleBuilder.save();
}
- void removeUser(String username) throws Exception {
+ public void removeUser(String username) {
if (userConfig.getProperty(username) == null) {
- throw new IllegalArgumentException("user " + username + " doesn't exist.");
+ throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
}
userConfig.clearProperty(username);
removeRoles(username);
}
- List<String> listUser(String username) {
- List<String> result = new ArrayList<>();
- result.add("--- \"user\"(roles) ---\n");
+ public Map<String, Set<String>> listUser(String username) {
+ Map<String, Set<String>> result = new HashMap<>();
- int totalUsers = 0;
- if (username != null) {
- String roles = findRoles(username);
- result.add("\"" + username + "\"(" + roles + ")");
- totalUsers++;
+ if (username != null && username.length() > 0) {
+ result.put(username, findRoles(username));
} else {
Iterator<String> iter = userConfig.getKeys();
while (iter.hasNext()) {
String keyUser = iter.next();
- String roles = findRoles(keyUser);
- result.add("\"" + keyUser + "\"(" + roles + ")");
- totalUsers++;
+ result.put(keyUser, findRoles(keyUser));
}
}
- result.add("\n Total: " + totalUsers);
return result;
}
- void updateUser(String username, String password, String[] roles) {
+ public void updateUser(String username, String password, String[] roles) {
String oldPassword = (String) userConfig.getProperty(username);
if (oldPassword == null) {
- throw new IllegalArgumentException("user " + username + " doesn't exist.");
+ throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
}
if (password != null) {
@@ -138,33 +178,28 @@ class FileBasedSecStoreConfig {
}
}
- private String findRoles(String uname) {
+ private Set<String> findRoles(String username) {
Iterator<String> iter = roleConfig.getKeys();
- StringBuilder builder = new StringBuilder();
- boolean first = true;
+ Set<String> roles = new HashSet();
while (iter.hasNext()) {
String role = iter.next();
- List<String> names = roleConfig.getList(String.class, role);
- for (String value : names) {
- //each value may be a comma separated list
- String[] items = value.split(",");
+ for (String roleList : roleConfig.getList(String.class, role)) {
+ //each roleList may be a comma separated list
+ String[] items = roleList.split(",");
for (String item : items) {
- if (item.equals(uname)) {
- if (!first) {
- builder.append(",");
- }
- builder.append(role);
- first = false;
+ if (item.equals(username)) {
+ roles.add(role);
}
}
}
}
- return builder.toString();
+ return roles;
}
private void addRoles(String username, String[] roles) {
for (String role : roles) {
+ role = role.trim();
List<String> users = roleConfig.getList(String.class, role);
if (users == null) {
users = new ArrayList<>();
diff --git a/docs/user-manual/en/security.md b/docs/user-manual/en/security.md
index 0ac37c4..ef0f363 100644
--- a/docs/user-manual/en/security.md
+++ b/docs/user-manual/en/security.md
@@ -546,6 +546,17 @@ users=system,user
guests=guest
```
+As mentioned above, the Artemis command-line interface supports a command to
+`add` a user. Commands to `list` (one or all) users, `remove` a user, and `reset`
+a user's password and/or role(s) are also supported via the command-line
+interface as well as the normal management interfaces (e.g. JMX, web console,
+etc.).
+
+> **Warning**
+>
+> Management and CLI operations to manipulate user & role data are only available
+> when using the `PropertiesLoginModule`.
+
#### LDAPLoginModule
The LDAP login module enables you to perform authentication and authorization
diff --git a/pom.xml b/pom.xml
index 3fe3697..3b82a86 100644
--- a/pom.xml
+++ b/pom.xml
@@ -77,7 +77,7 @@
<karaf.version>4.0.6</karaf.version>
<pax.exam.version>4.9.1</pax.exam.version>
- <commons.config.version>2.1</commons.config.version>
+ <commons.config.version>2.4</commons.config.version>
<commons.lang.version>3.0</commons.lang.version>
<activemq5-version>5.14.5</activemq5-version>
<apache.derby.version>10.11.1.1</apache.derby.version>
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java
index c74e6d5..847b01e 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java
@@ -2683,6 +2683,46 @@ public class ActiveMQServerControlTest extends ManagementTestBase {
Assert.assertTrue(((org.apache.activemq.artemis.jms.client.ActiveMQMessageConsumer)JMSclient).isClosed());
}
+ @Test
+ public void testAddUser() throws Exception {
+ ActiveMQServerControl serverControl = createManagementControl();
+ try {
+ serverControl.addUser("x", "x", "x", true, "x");
+ fail();
+ } catch (Exception expected) {
+ }
+ }
+
+ @Test
+ public void testRemoveUser() throws Exception {
+ ActiveMQServerControl serverControl = createManagementControl();
+ try {
+ serverControl.removeUser("x", "x");
+ fail();
+ } catch (Exception expected) {
+ }
+ }
+
+ @Test
+ public void testListUser() throws Exception {
+ ActiveMQServerControl serverControl = createManagementControl();
+ try {
+ serverControl.listUser("x", "x");
+ fail();
+ } catch (Exception expected) {
+ }
+ }
+
+ @Test
+ public void testResetUser() throws Exception {
+ ActiveMQServerControl serverControl = createManagementControl();
+ try {
+ serverControl.resetUser("x","x","x", "x");
+ fail();
+ } catch (Exception expected) {
+ }
+ }
+
protected void scaleDown(ScaleDownHandler handler) throws Exception {
SimpleString address = new SimpleString("testQueue");
diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java
index 716b5d0..f0315e8 100644
--- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java
+++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java
@@ -336,6 +336,31 @@ public class ActiveMQServerControlUsingCoreTest extends ActiveMQServerControlTes
}
@Override
+ public void addUser(String username,
+ String password,
+ String role,
+ boolean plaintext,
+ String entryName) throws Exception {
+ proxy.invokeOperation("addUser", username, password, role, plaintext, entryName);
+
+ }
+
+ @Override
+ public String listUser(String username, String entryName) throws Exception {
+ return (String) proxy.invokeOperation("listUser", username, entryName, String.class);
+ }
+
+ @Override
+ public void removeUser(String username, String entryName) throws Exception {
+ proxy.invokeOperation("removeUser", username, entryName);
+ }
+
+ @Override
+ public void resetUser(String username, String password, String roles, String entryName) throws Exception {
+ proxy.invokeOperation("resetUser", username, password, roles, entryName);
+ }
+
+ @Override
public String getUptime() {
return null;
}