ARTEMIS-2947 Implement SecurityManager that supports replication
This commit is contained in:
parent
caab2e1d4d
commit
75e12b5e1d
|
@ -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_PING_TXT = "etc/ping-settings.txt";
|
||||||
public static final String ETC_COMMENTED_PING_TXT = "etc/commented-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_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_SPECIFIED_TXT = "etc/global-max-specified.txt";
|
||||||
public static final String ETC_GLOBAL_MAX_DEFAULT_TXT = "etc/global-max-default.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")
|
@Option(name = "--jdbc-bindings-table-name", description = "Name of the jdbc bindigns table")
|
||||||
private String jdbcBindings = ActiveMQDefaultConfiguration.getDefaultBindingsTableName();
|
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
|
// we want this variable to remain unchanged so that it will use the value set in the profile
|
||||||
filters.remove("${artemis.instance}");
|
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_BOOTSTRAP_XML, etcFolder, filters, false);
|
||||||
writeEtc(ETC_MANAGEMENT_XML, etcFolder, filters, false);
|
writeEtc(ETC_MANAGEMENT_XML, etcFolder, filters, false);
|
||||||
|
|
||||||
|
@ -1193,4 +1203,20 @@ public class Create extends InputAbstract {
|
||||||
c = is.read(buffer);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,17 +72,15 @@ public class Run extends LockAbstract {
|
||||||
super.execute(context);
|
super.execute(context);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
BrokerDTO broker = getBrokerDTO();
|
||||||
|
ActiveMQSecurityManager securityManager = SecurityManagerFactory.create(broker.security);
|
||||||
ManagementContextDTO managementDTO = getManagementDTO();
|
ManagementContextDTO managementDTO = getManagementDTO();
|
||||||
managementContext = ManagementFactory.create(managementDTO);
|
managementContext = ManagementFactory.create(managementDTO, securityManager);
|
||||||
|
|
||||||
Artemis.printBanner();
|
Artemis.printBanner();
|
||||||
|
|
||||||
BrokerDTO broker = getBrokerDTO();
|
|
||||||
|
|
||||||
addShutdownHook(broker.server.getConfigurationFile().getParentFile());
|
addShutdownHook(broker.server.getConfigurationFile().getParentFile());
|
||||||
|
|
||||||
ActiveMQSecurityManager security = SecurityManagerFactory.create(broker.security);
|
|
||||||
|
|
||||||
ActivateCallback activateCallback = new ActivateCallback() {
|
ActivateCallback activateCallback = new ActivateCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void preActivate() {
|
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();
|
managementContext.start();
|
||||||
server.createComponents();
|
server.createComponents();
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.activemq.artemis.dto.JMXConnectorDTO;
|
||||||
import org.apache.activemq.artemis.dto.ManagementContextDTO;
|
import org.apache.activemq.artemis.dto.ManagementContextDTO;
|
||||||
import org.apache.activemq.artemis.dto.MatchDTO;
|
import org.apache.activemq.artemis.dto.MatchDTO;
|
||||||
import org.apache.activemq.artemis.core.server.management.JMXAccessControlList;
|
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 org.apache.activemq.artemis.utils.FactoryFinder;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -60,7 +61,7 @@ public class ManagementFactory {
|
||||||
return createJmxAclConfiguration(new URI(configuration), artemisHome, artemisInstance, artemisURIInstance);
|
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();
|
ManagementContext context = new ManagementContext();
|
||||||
|
|
||||||
if (config.getAuthorisation() != null) {
|
if (config.getAuthorisation() != null) {
|
||||||
|
@ -130,6 +131,7 @@ public class ManagementFactory {
|
||||||
jmxConnectorConfiguration.setSecured(jmxConnector.isSecured());
|
jmxConnectorConfiguration.setSecured(jmxConnector.isSecured());
|
||||||
}
|
}
|
||||||
context.setJmxConnectorConfiguration(jmxConnectorConfiguration);
|
context.setJmxConnectorConfiguration(jmxConnectorConfiguration);
|
||||||
|
context.setSecurityManager(securityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
|
|
|
@ -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>
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<broker xmlns="http://activemq.org/schema">
|
<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.
|
<!-- 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 -->
|
This is to avoid situations where you could have spaces or special characters on this URI -->
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<jaas-security domain="activemq"/>
|
|
@ -105,6 +105,9 @@ import static org.junit.Assert.fail;
|
||||||
public class ArtemisTest extends CliTestBase {
|
public class ArtemisTest extends CliTestBase {
|
||||||
private static final Logger log = Logger.getLogger(ArtemisTest.class);
|
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
|
@Before
|
||||||
@Override
|
@Override
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
|
@ -340,13 +343,23 @@ public class ArtemisTest extends CliTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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);
|
Run.setEmbedded(true);
|
||||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
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());
|
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||||
Artemis.internalExecute("run");
|
Object runResult = Artemis.internalExecute("run");
|
||||||
|
server = ((Pair<ManagementContext, ActiveMQServer>)runResult).getB();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
|
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
|
//default only one user admin with role amq
|
||||||
assertTrue(result.contains("\"admin\"(amq)"));
|
assertTrue(result.contains("\"admin\"(amq)"));
|
||||||
checkRole("admin", roleFile, "amq");
|
checkRole("admin", roleFile, basic, "amq");
|
||||||
|
|
||||||
//add a simple user
|
//add a simple user
|
||||||
AddUser addCmd = new AddUser();
|
AddUser addCmd = new AddUser();
|
||||||
|
@ -383,8 +396,8 @@ public class ArtemisTest extends CliTestBase {
|
||||||
assertTrue(result.contains("\"admin\"(amq)"));
|
assertTrue(result.contains("\"admin\"(amq)"));
|
||||||
assertTrue(result.contains("\"guest\"(admin)"));
|
assertTrue(result.contains("\"guest\"(admin)"));
|
||||||
|
|
||||||
checkRole("guest", roleFile, "admin");
|
checkRole("guest", roleFile, basic, "admin");
|
||||||
assertTrue(checkPassword("guest", "guest123", userFile));
|
assertTrue(checkPassword("guest", "guest123", userFile, basic));
|
||||||
|
|
||||||
//add a user with 2 roles
|
//add a user with 2 roles
|
||||||
addCmd = new AddUser();
|
addCmd = new AddUser();
|
||||||
|
@ -405,8 +418,8 @@ public class ArtemisTest extends CliTestBase {
|
||||||
assertTrue(result.contains("\"guest\"(admin)"));
|
assertTrue(result.contains("\"guest\"(admin)"));
|
||||||
assertTrue(result.contains("\"scott\"(admin,operator)"));
|
assertTrue(result.contains("\"scott\"(admin,operator)"));
|
||||||
|
|
||||||
checkRole("scott", roleFile, "admin", "operator");
|
checkRole("scott", roleFile, basic, "admin", "operator");
|
||||||
assertTrue(checkPassword("scott", "tiger", userFile));
|
assertTrue(checkPassword("scott", "tiger", userFile, basic));
|
||||||
|
|
||||||
//add an existing user
|
//add an existing user
|
||||||
addCmd = new AddUser();
|
addCmd = new AddUser();
|
||||||
|
@ -504,24 +517,34 @@ public class ArtemisTest extends CliTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserCommandViaManagementPlaintext() throws Exception {
|
public void testUserCommandViaManagementPlaintextJAAS() throws Exception {
|
||||||
internalTestUserCommandViaManagement(true);
|
internalTestUserCommandViaManagement(true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserCommandViaManagementHashed() throws Exception {
|
public void testUserCommandViaManagementHashedJAAS() throws Exception {
|
||||||
internalTestUserCommandViaManagement(false);
|
internalTestUserCommandViaManagement(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void internalTestUserCommandViaManagement(boolean plaintext) throws Exception {
|
@Test
|
||||||
|
public void testUserCommandViaManagementPlaintextBasic() throws Exception {
|
||||||
|
internalTestUserCommandViaManagement(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUserCommandViaManagementHashedBasic() throws Exception {
|
||||||
|
internalTestUserCommandViaManagement(false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void internalTestUserCommandViaManagement(boolean plaintext, boolean basic) throws Exception {
|
||||||
Run.setEmbedded(true);
|
Run.setEmbedded(true);
|
||||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
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());
|
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||||
Object result = Artemis.internalExecute("run");
|
Object result = Artemis.internalExecute("run");
|
||||||
ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
|
server = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
|
||||||
ActiveMQServerControl activeMQServerControl = activeMQServer.getActiveMQServerControl();
|
ActiveMQServerControl activeMQServerControl = server.getActiveMQServerControl();
|
||||||
|
|
||||||
File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
|
File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
|
||||||
File roleFile = new File(instance1.getAbsolutePath() + "/etc/artemis-roles.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
|
//default only one user admin with role amq
|
||||||
String jsonResult = activeMQServerControl.listUser("");
|
String jsonResult = activeMQServerControl.listUser("");
|
||||||
contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
|
contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
|
||||||
checkRole("admin", roleFile, "amq");
|
checkRole("admin", roleFile, basic, "amq");
|
||||||
|
|
||||||
//add a simple user
|
//add a simple user
|
||||||
activeMQServerControl.addUser("guest", "guest123", "admin", plaintext);
|
activeMQServerControl.addUser("guest", "guest123", "admin", plaintext);
|
||||||
|
@ -537,9 +560,9 @@ public class ArtemisTest extends CliTestBase {
|
||||||
//verify add
|
//verify add
|
||||||
jsonResult = activeMQServerControl.listUser("");
|
jsonResult = activeMQServerControl.listUser("");
|
||||||
contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin");
|
contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin");
|
||||||
checkRole("guest", roleFile, "admin");
|
checkRole("guest", roleFile, basic, "admin");
|
||||||
assertTrue(checkPassword("guest", "guest123", userFile));
|
assertTrue(checkPassword("guest", "guest123", userFile, basic));
|
||||||
assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("guest", userFile)));
|
assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("guest", userFile, basic)));
|
||||||
|
|
||||||
//add a user with 2 roles
|
//add a user with 2 roles
|
||||||
activeMQServerControl.addUser("scott", "tiger", "admin,operator", plaintext);
|
activeMQServerControl.addUser("scott", "tiger", "admin,operator", plaintext);
|
||||||
|
@ -548,9 +571,9 @@ public class ArtemisTest extends CliTestBase {
|
||||||
jsonResult = activeMQServerControl.listUser("");
|
jsonResult = activeMQServerControl.listUser("");
|
||||||
contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin");
|
contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin");
|
||||||
contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator");
|
contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator");
|
||||||
checkRole("scott", roleFile, "admin", "operator");
|
checkRole("scott", roleFile, basic, "admin", "operator");
|
||||||
assertTrue(checkPassword("scott", "tiger", userFile));
|
assertTrue(checkPassword("scott", "tiger", userFile, basic));
|
||||||
assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("scott", userFile)));
|
assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("scott", userFile, basic)));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
activeMQServerControl.addUser("scott", "password", "visitor", plaintext);
|
activeMQServerControl.addUser("scott", "password", "visitor", plaintext);
|
||||||
|
@ -615,11 +638,20 @@ public class ArtemisTest extends CliTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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);
|
Run.setEmbedded(true);
|
||||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
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());
|
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||||
Object result = Artemis.internalExecute("run");
|
Object result = Artemis.internalExecute("run");
|
||||||
ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
|
ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
|
||||||
|
@ -702,13 +734,23 @@ public class ArtemisTest extends CliTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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);
|
Run.setEmbedded(true);
|
||||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
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());
|
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||||
Artemis.internalExecute("run");
|
Object runResult = Artemis.internalExecute("run");
|
||||||
|
server = ((Pair<ManagementContext, ActiveMQServer>)runResult).getB();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
|
File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
|
||||||
|
@ -754,7 +796,7 @@ public class ArtemisTest extends CliTestBase {
|
||||||
addCmd.setUserCommandPassword("password1");
|
addCmd.setUserCommandPassword("password1");
|
||||||
addCmd.setRole("admin,manager");
|
addCmd.setRole("admin,manager");
|
||||||
addCmd.execute(new TestActionContext());
|
addCmd.execute(new TestActionContext());
|
||||||
assertTrue(checkPassword("user1", "password1", userFile));
|
assertTrue(checkPassword("user1", "password1", userFile, basic));
|
||||||
|
|
||||||
addCmd.setUserCommandUser("user2");
|
addCmd.setUserCommandUser("user2");
|
||||||
addCmd.setUserCommandPassword("password2");
|
addCmd.setUserCommandPassword("password2");
|
||||||
|
@ -787,7 +829,7 @@ public class ArtemisTest extends CliTestBase {
|
||||||
.matcher(result)
|
.matcher(result)
|
||||||
.find());
|
.find());
|
||||||
|
|
||||||
checkRole("user1", roleFile, "admin", "manager");
|
checkRole("user1", roleFile, basic, "admin", "manager");
|
||||||
|
|
||||||
//reset password
|
//reset password
|
||||||
context = new TestActionContext();
|
context = new TestActionContext();
|
||||||
|
@ -798,16 +840,16 @@ public class ArtemisTest extends CliTestBase {
|
||||||
resetCommand.setPassword("admin");
|
resetCommand.setPassword("admin");
|
||||||
resetCommand.execute(context);
|
resetCommand.execute(context);
|
||||||
|
|
||||||
checkRole("user1", roleFile, "admin", "manager");
|
checkRole("user1", roleFile, basic, "admin", "manager");
|
||||||
assertFalse(checkPassword("user1", "password1", userFile));
|
assertFalse(checkPassword("user1", "password1", userFile, basic));
|
||||||
assertTrue(checkPassword("user1", "newpassword1", userFile));
|
assertTrue(checkPassword("user1", "newpassword1", userFile, basic));
|
||||||
|
|
||||||
//reset role
|
//reset role
|
||||||
resetCommand.setUserCommandUser("user2");
|
resetCommand.setUserCommandUser("user2");
|
||||||
resetCommand.setRole("manager,master,operator");
|
resetCommand.setRole("manager,master,operator");
|
||||||
resetCommand.execute(new TestActionContext());
|
resetCommand.execute(new TestActionContext());
|
||||||
|
|
||||||
checkRole("user2", roleFile, "manager", "master", "operator");
|
checkRole("user2", roleFile, basic, "manager", "master", "operator");
|
||||||
|
|
||||||
//reset both
|
//reset both
|
||||||
resetCommand.setUserCommandUser("user3");
|
resetCommand.setUserCommandUser("user3");
|
||||||
|
@ -815,19 +857,28 @@ public class ArtemisTest extends CliTestBase {
|
||||||
resetCommand.setRole("admin,system");
|
resetCommand.setRole("admin,system");
|
||||||
resetCommand.execute(new ActionContext());
|
resetCommand.execute(new ActionContext());
|
||||||
|
|
||||||
checkRole("user3", roleFile, "admin", "system");
|
checkRole("user3", roleFile, basic, "admin", "system");
|
||||||
assertTrue(checkPassword("user3", "newpassword3", userFile));
|
assertTrue(checkPassword("user3", "newpassword3", userFile, basic));
|
||||||
} finally {
|
} finally {
|
||||||
stopServer();
|
stopServer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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);
|
Run.setEmbedded(true);
|
||||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
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());
|
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||||
Artemis.internalExecute("run");
|
Artemis.internalExecute("run");
|
||||||
|
|
||||||
|
@ -1711,28 +1762,52 @@ public class ArtemisTest extends CliTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkRole(String user, File roleFile, String... roles) throws Exception {
|
private void checkRole(String user, File roleFile, String... roles) throws Exception {
|
||||||
Configurations configs = new Configurations();
|
checkRole(user, roleFile, false, roles);
|
||||||
FileBasedConfigurationBuilder<PropertiesConfiguration> roleBuilder = configs.propertiesBuilder(roleFile);
|
}
|
||||||
PropertiesConfiguration roleConfig = roleBuilder.getConfiguration();
|
|
||||||
|
|
||||||
for (String r : roles) {
|
private void checkRole(String user, File roleFile, boolean basicSecurityManager, String... roles) throws Exception {
|
||||||
String storedUsers = (String) roleConfig.getProperty(r);
|
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();
|
||||||
|
|
||||||
log.debug("users in role: " + r + " ; " + storedUsers);
|
for (String r : roles) {
|
||||||
List<String> userList = StringUtil.splitStringList(storedUsers, ",");
|
String storedUsers = (String) roleConfig.getProperty(r);
|
||||||
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 {
|
private String getStoredPassword(String user, File userFile) throws Exception {
|
||||||
Configurations configs = new Configurations();
|
return getStoredPassword(user, userFile, false);
|
||||||
FileBasedConfigurationBuilder<PropertiesConfiguration> userBuilder = configs.propertiesBuilder(userFile);
|
}
|
||||||
PropertiesConfiguration userConfig = userBuilder.getConfiguration();
|
|
||||||
return (String) userConfig.getProperty(user);
|
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 {
|
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);
|
HashProcessor processor = PasswordMaskingUtil.getHashProcessor(storedPassword);
|
||||||
return processor.compare(password.toCharArray(), storedPassword);
|
return processor.compare(password.toCharArray(), storedPassword);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,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.
|
* 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
|
* @param maskPassword : explicit mask flag. If it's true, the password is interpreted as
|
||||||
|
@ -109,7 +121,7 @@ public final class PasswordMaskingUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
//stored password takes 2 forms, ENC() or plain text
|
//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)) {
|
if (!isEncoded(storedPassword)) {
|
||||||
return LazyPlainTextProcessorHolder.INSTANCE;
|
return LazyPlainTextProcessorHolder.INSTANCE;
|
||||||
|
|
|
@ -1830,45 +1830,45 @@ public interface ActiveMQServerControl {
|
||||||
* @param roles
|
* @param roles
|
||||||
* @throws Exception
|
* @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,
|
void addUser(@Parameter(name = "username", desc = "Name of the user") String username,
|
||||||
@Parameter(name = "password", desc = "User's password") String password,
|
@Parameter(name = "password", desc = "User's password") String password,
|
||||||
@Parameter(name = "roles", desc = "User's role (comma separated)") String roles,
|
@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;
|
@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
|
* @param username
|
||||||
* @return JSON array of user and role information
|
* @return JSON array of user and role information
|
||||||
* @throws Exception
|
* @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;
|
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
|
* @param username
|
||||||
* @throws Exception
|
* @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;
|
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 username
|
||||||
* @param password
|
* @param password
|
||||||
* @param roles
|
* @param roles
|
||||||
* @throws Exception
|
* @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,
|
void resetUser(@Parameter(name = "username", desc = "Name of the user") String username,
|
||||||
@Parameter(name = "password", desc = "User's password") String password,
|
@Parameter(name = "password", desc = "User's password") String password,
|
||||||
@Parameter(name = "roles", desc = "User's role (comma separated)") String roles) throws Exception;
|
@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 username
|
||||||
* @param password
|
* @param password
|
||||||
|
@ -1877,7 +1877,7 @@ public interface ActiveMQServerControl {
|
||||||
* @throws Exception
|
* @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,
|
void resetUser(@Parameter(name = "username", desc = "Name of the user") String username,
|
||||||
@Parameter(name = "password", desc = "User's password") String password,
|
@Parameter(name = "password", desc = "User's password") String password,
|
||||||
@Parameter(name = "roles", desc = "User's role (comma separated)") String roles,
|
@Parameter(name = "roles", desc = "User's role (comma separated)") String roles,
|
||||||
|
|
|
@ -123,6 +123,6 @@ public class JMXConnectorDTO {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPassword(String password) throws Exception {
|
private String getPassword(String password) throws Exception {
|
||||||
return PasswordMaskingUtil.resolveMask(null, password, this.passwordCodec);
|
return PasswordMaskingUtil.resolveMask(password, this.passwordCodec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class WebServerDTO extends ComponentDTO {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPassword(String password) throws Exception {
|
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) {
|
public void setKeyStorePassword(String keyStorePassword) {
|
||||||
|
|
|
@ -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.StorageManager;
|
||||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
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.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.Binding;
|
||||||
import org.apache.activemq.artemis.core.postoffice.Bindings;
|
import org.apache.activemq.artemis.core.postoffice.Bindings;
|
||||||
import org.apache.activemq.artemis.core.postoffice.DuplicateIDCache;
|
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.ActiveMQMessageBundle;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
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.ConnectorServiceFactory;
|
||||||
import org.apache.activemq.artemis.core.server.Consumer;
|
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.Divert;
|
||||||
import org.apache.activemq.artemis.core.server.JournalType;
|
import org.apache.activemq.artemis.core.server.JournalType;
|
||||||
import org.apache.activemq.artemis.core.server.Queue;
|
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.core.transaction.impl.XidImpl;
|
||||||
import org.apache.activemq.artemis.logs.AuditLogger;
|
import org.apache.activemq.artemis.logs.AuditLogger;
|
||||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
|
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.spi.core.security.jaas.PropertiesLoginModuleConfigurator;
|
||||||
import org.apache.activemq.artemis.utils.JsonLoader;
|
import org.apache.activemq.artemis.utils.JsonLoader;
|
||||||
import org.apache.activemq.artemis.utils.ListUtil;
|
import org.apache.activemq.artemis.utils.ListUtil;
|
||||||
|
@ -2784,9 +2785,9 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
|
||||||
|
|
||||||
server.getSecurityRepository().addMatch(addressMatch, roles);
|
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 {
|
} finally {
|
||||||
blockOnIO();
|
blockOnIO();
|
||||||
}
|
}
|
||||||
|
@ -2802,7 +2803,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
|
||||||
clearIO();
|
clearIO();
|
||||||
try {
|
try {
|
||||||
server.getSecurityRepository().removeMatch(addressMatch);
|
server.getSecurityRepository().removeMatch(addressMatch);
|
||||||
storageManager.deleteSecurityRoles(new SimpleString(addressMatch));
|
storageManager.deleteSecuritySetting(new SimpleString(addressMatch));
|
||||||
} finally {
|
} finally {
|
||||||
blockOnIO();
|
blockOnIO();
|
||||||
}
|
}
|
||||||
|
@ -4189,15 +4190,22 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addUser(String username, String password, String roles, boolean plaintext) throws Exception {
|
public void addUser(String username, String password, String roles, boolean plaintext) throws Exception {
|
||||||
synchronized (userLock) {
|
if (AuditLogger.isEnabled()) {
|
||||||
if (AuditLogger.isEnabled()) {
|
AuditLogger.addUser(this.server, username, "****", roles, plaintext);
|
||||||
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
|
@Override
|
||||||
public String listUser(String username) throws Exception {
|
public String listUser(String username) throws Exception {
|
||||||
synchronized (userLock) {
|
if (AuditLogger.isEnabled()) {
|
||||||
if (AuditLogger.isEnabled()) {
|
AuditLogger.listUser(this.server, username);
|
||||||
AuditLogger.listUser(this.server, 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();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
@Override
|
||||||
public void removeUser(String username) throws Exception {
|
public void removeUser(String username) throws Exception {
|
||||||
synchronized (userLock) {
|
if (AuditLogger.isEnabled()) {
|
||||||
if (AuditLogger.isEnabled()) {
|
AuditLogger.removeUser(this.server, username);
|
||||||
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
|
@Override
|
||||||
public void resetUser(String username, String password, String roles, boolean plaintext) throws Exception {
|
public void resetUser(String username, String password, String roles, boolean plaintext) throws Exception {
|
||||||
synchronized (userLock) {
|
if (AuditLogger.isEnabled()) {
|
||||||
if (AuditLogger.isEnabled()) {
|
AuditLogger.resetUser(this.server, username, "****", roles, plaintext);
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.paging.cursor.PagePosition;
|
||||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
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.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.PageCountPending;
|
||||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||||
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
||||||
|
@ -354,17 +356,30 @@ public interface StorageManager extends IDGenerator, ActiveMQComponent {
|
||||||
|
|
||||||
List<PersistedAddressSetting> recoverAddressSettings() throws Exception;
|
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 storeDivertConfiguration(PersistedDivertConfiguration persistedDivertConfiguration) throws Exception;
|
||||||
|
|
||||||
void deleteDivertConfiguration(String divertName) throws Exception;
|
void deleteDivertConfiguration(String divertName) throws Exception;
|
||||||
|
|
||||||
List<PersistedDivertConfiguration> recoverDivertConfigurations();
|
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
|
* @return The ID with the stored counter
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.api.core.SimpleString;
|
||||||
import org.apache.activemq.artemis.core.journal.EncodingSupport;
|
import org.apache.activemq.artemis.core.journal.EncodingSupport;
|
||||||
|
|
||||||
public class PersistedRoles implements EncodingSupport {
|
public class PersistedSecuritySetting implements EncodingSupport {
|
||||||
|
|
||||||
// Constants -----------------------------------------------------
|
// Constants -----------------------------------------------------
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ public class PersistedRoles implements EncodingSupport {
|
||||||
|
|
||||||
// Constructors --------------------------------------------------
|
// Constructors --------------------------------------------------
|
||||||
|
|
||||||
public PersistedRoles() {
|
public PersistedSecuritySetting() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,17 +70,17 @@ public class PersistedRoles implements EncodingSupport {
|
||||||
* @param createAddressRoles
|
* @param createAddressRoles
|
||||||
* @param deleteAddressRoles
|
* @param deleteAddressRoles
|
||||||
*/
|
*/
|
||||||
public PersistedRoles(final String addressMatch,
|
public PersistedSecuritySetting(final String addressMatch,
|
||||||
final String sendRoles,
|
final String sendRoles,
|
||||||
final String consumeRoles,
|
final String consumeRoles,
|
||||||
final String createDurableQueueRoles,
|
final String createDurableQueueRoles,
|
||||||
final String deleteDurableQueueRoles,
|
final String deleteDurableQueueRoles,
|
||||||
final String createNonDurableQueueRoles,
|
final String createNonDurableQueueRoles,
|
||||||
final String deleteNonDurableQueueRoles,
|
final String deleteNonDurableQueueRoles,
|
||||||
final String manageRoles,
|
final String manageRoles,
|
||||||
final String browseRoles,
|
final String browseRoles,
|
||||||
final String createAddressRoles,
|
final String createAddressRoles,
|
||||||
final String deleteAddressRoles) {
|
final String deleteAddressRoles) {
|
||||||
super();
|
super();
|
||||||
this.addressMatch = SimpleString.toSimpleString(addressMatch);
|
this.addressMatch = SimpleString.toSimpleString(addressMatch);
|
||||||
this.sendRoles = SimpleString.toSimpleString(sendRoles);
|
this.sendRoles = SimpleString.toSimpleString(sendRoles);
|
||||||
|
@ -259,7 +259,7 @@ public class PersistedRoles implements EncodingSupport {
|
||||||
return false;
|
return false;
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass())
|
||||||
return false;
|
return false;
|
||||||
PersistedRoles other = (PersistedRoles) obj;
|
PersistedSecuritySetting other = (PersistedSecuritySetting) obj;
|
||||||
if (addressMatch == null) {
|
if (addressMatch == null) {
|
||||||
if (other.addressMatch != null)
|
if (other.addressMatch != null)
|
||||||
return false;
|
return false;
|
||||||
|
@ -325,7 +325,7 @@ public class PersistedRoles implements EncodingSupport {
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PersistedRoles [storeId=" + storeId +
|
return "PersistedSecuritySetting [storeId=" + storeId +
|
||||||
", addressMatch=" +
|
", addressMatch=" +
|
||||||
addressMatch +
|
addressMatch +
|
||||||
", sendRoles=" +
|
", sendRoles=" +
|
|
@ -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=****" +
|
||||||
|
"]";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.StorageManager;
|
||||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
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.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.PageCountPending;
|
||||||
import org.apache.activemq.artemis.core.persistence.impl.journal.codec.AddressStatusEncoding;
|
import org.apache.activemq.artemis.core.persistence.impl.journal.codec.AddressStatusEncoding;
|
||||||
import org.apache.activemq.artemis.core.persistence.impl.journal.codec.CursorAckRecordEncoding;
|
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;
|
protected final Configuration config;
|
||||||
|
|
||||||
// Persisted core configuration
|
// 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<SimpleString, PersistedAddressSetting> mapPersistedAddressSettings = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
protected final Map<String, PersistedDivertConfiguration> mapPersistedDivertConfigurations = 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<>();
|
protected final ConcurrentLongHashMap<LargeServerMessage> largeMessagesToDelete = new ConcurrentLongHashMap<>();
|
||||||
|
|
||||||
public AbstractJournalStorageManager(final Configuration config,
|
public AbstractJournalStorageManager(final Configuration config,
|
||||||
|
@ -767,20 +773,20 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PersistedRoles> recoverPersistedRoles() throws Exception {
|
public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
|
||||||
return new ArrayList<>(mapPersistedRoles.values());
|
return new ArrayList<>(mapPersistedSecuritySettings.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
|
public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
|
||||||
|
|
||||||
deleteSecurityRoles(persistedRoles.getAddressMatch());
|
deleteSecuritySetting(persistedRoles.getAddressMatch());
|
||||||
readLock();
|
readLock();
|
||||||
try {
|
try {
|
||||||
final long id = idGenerator.generateID();
|
final long id = idGenerator.generateID();
|
||||||
persistedRoles.setStoreId(id);
|
persistedRoles.setStoreId(id);
|
||||||
bindingsJournal.appendAddRecord(id, JournalRecordIds.SECURITY_RECORD, persistedRoles, true);
|
bindingsJournal.appendAddRecord(id, JournalRecordIds.SECURITY_SETTING_RECORD, persistedRoles, true);
|
||||||
mapPersistedRoles.put(persistedRoles.getAddressMatch(), persistedRoles);
|
mapPersistedSecuritySettings.put(persistedRoles.getAddressMatch(), persistedRoles);
|
||||||
} finally {
|
} finally {
|
||||||
readUnLock();
|
readUnLock();
|
||||||
}
|
}
|
||||||
|
@ -818,6 +824,70 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
||||||
return new ArrayList<>(mapPersistedDivertConfigurations.values());
|
return new ArrayList<>(mapPersistedDivertConfigurations.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Override
|
||||||
public void storeID(final long journalID, final long id) throws Exception {
|
public void storeID(final long journalID, final long id) throws Exception {
|
||||||
readLock();
|
readLock();
|
||||||
|
@ -852,8 +922,8 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
|
public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
|
||||||
PersistedRoles oldRoles = mapPersistedRoles.remove(addressMatch);
|
PersistedSecuritySetting oldRoles = mapPersistedSecuritySettings.remove(addressMatch);
|
||||||
if (oldRoles != null) {
|
if (oldRoles != null) {
|
||||||
readLock();
|
readLock();
|
||||||
try {
|
try {
|
||||||
|
@ -1560,9 +1630,9 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
||||||
} else if (rec == JournalRecordIds.ADDRESS_SETTING_RECORD) {
|
} else if (rec == JournalRecordIds.ADDRESS_SETTING_RECORD) {
|
||||||
PersistedAddressSetting setting = newAddressEncoding(id, buffer);
|
PersistedAddressSetting setting = newAddressEncoding(id, buffer);
|
||||||
mapPersistedAddressSettings.put(setting.getAddressMatch(), setting);
|
mapPersistedAddressSettings.put(setting.getAddressMatch(), setting);
|
||||||
} else if (rec == JournalRecordIds.SECURITY_RECORD) {
|
} else if (rec == JournalRecordIds.SECURITY_SETTING_RECORD) {
|
||||||
PersistedRoles roles = newSecurityRecord(id, buffer);
|
PersistedSecuritySetting roles = newSecurityRecord(id, buffer);
|
||||||
mapPersistedRoles.put(roles.getAddressMatch(), roles);
|
mapPersistedSecuritySettings.put(roles.getAddressMatch(), roles);
|
||||||
} else if (rec == JournalRecordIds.QUEUE_STATUS_RECORD) {
|
} else if (rec == JournalRecordIds.QUEUE_STATUS_RECORD) {
|
||||||
QueueStatusEncoding statusEncoding = newQueueStatusEncoding(id, buffer);
|
QueueStatusEncoding statusEncoding = newQueueStatusEncoding(id, buffer);
|
||||||
PersistentQueueBindingEncoding queueBindingEncoding = mapBindings.get(statusEncoding.queueID);
|
PersistentQueueBindingEncoding queueBindingEncoding = mapBindings.get(statusEncoding.queueID);
|
||||||
|
@ -1586,6 +1656,12 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
||||||
} else if (rec == JournalRecordIds.DIVERT_RECORD) {
|
} else if (rec == JournalRecordIds.DIVERT_RECORD) {
|
||||||
PersistedDivertConfiguration divertConfiguration = newDivertEncoding(id, buffer);
|
PersistedDivertConfiguration divertConfiguration = newDivertEncoding(id, buffer);
|
||||||
mapPersistedDivertConfigurations.put(divertConfiguration.getName(), divertConfiguration);
|
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 {
|
} else {
|
||||||
// unlikely to happen
|
// unlikely to happen
|
||||||
ActiveMQServerLogger.LOGGER.invalidRecordType(rec, new Exception("invalid record type " + rec));
|
ActiveMQServerLogger.LOGGER.invalidRecordType(rec, new Exception("invalid record type " + rec));
|
||||||
|
@ -2042,8 +2118,8 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
||||||
* @param buffer
|
* @param buffer
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected static PersistedRoles newSecurityRecord(long id, ActiveMQBuffer buffer) {
|
protected static PersistedSecuritySetting newSecurityRecord(long id, ActiveMQBuffer buffer) {
|
||||||
PersistedRoles roles = new PersistedRoles();
|
PersistedSecuritySetting roles = new PersistedSecuritySetting();
|
||||||
roles.decode(buffer);
|
roles.decode(buffer);
|
||||||
roles.setStoreId(id);
|
roles.setStoreId(id);
|
||||||
return roles;
|
return roles;
|
||||||
|
@ -2074,6 +2150,20 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
||||||
persistedDivertConfiguration.setStoreId(id);
|
persistedDivertConfiguration.setStoreId(id);
|
||||||
return persistedDivertConfiguration;
|
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 id
|
||||||
* @param buffer
|
* @param buffer
|
||||||
|
|
|
@ -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.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_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.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.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.UPDATE_DELIVERY_COUNT;
|
||||||
|
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.USER_RECORD;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outputs a String description of the Journals contents.
|
* Outputs a String description of the Journals contents.
|
||||||
|
@ -685,7 +687,7 @@ public final class DescribeJournal {
|
||||||
case ADDRESS_SETTING_RECORD:
|
case ADDRESS_SETTING_RECORD:
|
||||||
return AbstractJournalStorageManager.newAddressEncoding(id, buffer);
|
return AbstractJournalStorageManager.newAddressEncoding(id, buffer);
|
||||||
|
|
||||||
case SECURITY_RECORD:
|
case SECURITY_SETTING_RECORD:
|
||||||
return AbstractJournalStorageManager.newSecurityRecord(id, buffer);
|
return AbstractJournalStorageManager.newSecurityRecord(id, buffer);
|
||||||
|
|
||||||
case ADDRESS_BINDING_RECORD:
|
case ADDRESS_BINDING_RECORD:
|
||||||
|
@ -694,6 +696,12 @@ public final class DescribeJournal {
|
||||||
case ADDRESS_STATUS_RECORD:
|
case ADDRESS_STATUS_RECORD:
|
||||||
return AbstractJournalStorageManager.newAddressStatusEncoding(id, buffer);
|
return AbstractJournalStorageManager.newAddressStatusEncoding(id, buffer);
|
||||||
|
|
||||||
|
case USER_RECORD:
|
||||||
|
return AbstractJournalStorageManager.newUserEncoding(id, buffer);
|
||||||
|
|
||||||
|
case ROLE_RECORD:
|
||||||
|
return AbstractJournalStorageManager.newRoleEncoding(id, buffer);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ public final class JournalRecordIds {
|
||||||
|
|
||||||
public static final byte ADDRESS_SETTING_RECORD = 25;
|
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;
|
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 ADDRESS_STATUS_RECORD = 46;
|
||||||
|
|
||||||
|
public static final byte USER_RECORD = 47;
|
||||||
|
|
||||||
|
public static final byte ROLE_RECORD = 48;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.StorageManager;
|
||||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
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.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.PageCountPending;
|
||||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||||
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
||||||
|
@ -438,7 +440,7 @@ public class NullStorageManager implements StorageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PersistedRoles> recoverPersistedRoles() throws Exception {
|
public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +458,33 @@ public class NullStorageManager implements StorageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
|
@ -464,7 +492,7 @@ public class NullStorageManager implements StorageManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSecurityRoles(final SimpleString addressMatch) throws Exception {
|
public void deleteSecuritySetting(final SimpleString addressMatch) throws Exception {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,11 +16,13 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.activemq.artemis.core.security;
|
package org.apache.activemq.artemis.core.security;
|
||||||
|
|
||||||
|
import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
|
||||||
|
|
||||||
public class User {
|
public class User {
|
||||||
|
|
||||||
final String user;
|
final String user;
|
||||||
|
|
||||||
final String password;
|
String password;
|
||||||
|
|
||||||
public User(final String user, final String password) {
|
public User(final String user, final String password) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
|
@ -54,7 +56,8 @@ public class User {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
return false;
|
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() {
|
public String getUser() {
|
||||||
|
@ -64,4 +67,8 @@ public class User {
|
||||||
public String getPassword() {
|
public String getPassword() {
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -495,4 +495,7 @@ public interface ActiveMQMessageBundle {
|
||||||
|
|
||||||
@Message(id = 229232, value = "Cannot create consumer on {0}. Session is closed.", format = Message.Format.MESSAGE_FORMAT)
|
@Message(id = 229232, value = "Cannot create consumer on {0}. Session is closed.", format = Message.Format.MESSAGE_FORMAT)
|
||||||
ActiveMQIllegalStateException cannotCreateConsumerOnClosedSession(SimpleString queueName);
|
ActiveMQIllegalStateException cannotCreateConsumerOnClosedSession(SimpleString queueName);
|
||||||
|
|
||||||
|
@Message(id = 229233, value = "Cannot set ActiveMQSecurityManager during startup or while started")
|
||||||
|
IllegalStateException cannotSetSecurityManager();
|
||||||
}
|
}
|
||||||
|
|
|
@ -833,6 +833,10 @@ public interface ActiveMQServer extends ServiceComponent {
|
||||||
|
|
||||||
void setMBeanServer(MBeanServer mBeanServer);
|
void setMBeanServer(MBeanServer mBeanServer);
|
||||||
|
|
||||||
|
MBeanServer getMBeanServer();
|
||||||
|
|
||||||
|
void setSecurityManager(ActiveMQSecurityManager securityManager);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adding external components is allowed only if the state
|
* Adding external components is allowed only if the state
|
||||||
* isn't {@link SERVER_STATE#STOPPED} or {@link SERVER_STATE#STOPPING}.<br>
|
* isn't {@link SERVER_STATE#STOPPED} or {@link SERVER_STATE#STOPPING}.<br>
|
||||||
|
|
|
@ -1717,6 +1717,14 @@ public interface ActiveMQServerLogger extends BasicLogger {
|
||||||
format = Message.Format.MESSAGE_FORMAT)
|
format = Message.Format.MESSAGE_FORMAT)
|
||||||
void unableStartManagementContext(@Cause Exception e);
|
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)
|
@LogMessage(level = Logger.Level.ERROR)
|
||||||
@Message(id = 224000, value = "Failure in initialisation", format = Message.Format.MESSAGE_FORMAT)
|
@Message(id = 224000, value = "Failure in initialisation", format = Message.Format.MESSAGE_FORMAT)
|
||||||
void initializationError(@Cause Throwable e);
|
void initializationError(@Cause Throwable e);
|
||||||
|
|
|
@ -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.StorageManager;
|
||||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
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.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.PageCountPending;
|
||||||
import org.apache.activemq.artemis.core.persistence.impl.journal.JDBCJournalStorageManager;
|
import org.apache.activemq.artemis.core.persistence.impl.journal.JDBCJournalStorageManager;
|
||||||
import org.apache.activemq.artemis.core.persistence.impl.journal.JournalStorageManager;
|
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.ProtocolManagerFactory;
|
||||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
|
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.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.spi.core.security.ActiveMQSecurityManager;
|
||||||
import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
|
import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
|
||||||
import org.apache.activemq.artemis.utils.ActiveMQThreadPoolExecutor;
|
import org.apache.activemq.artemis.utils.ActiveMQThreadPoolExecutor;
|
||||||
|
@ -236,7 +237,7 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
||||||
|
|
||||||
private final Version version;
|
private final Version version;
|
||||||
|
|
||||||
private final ActiveMQSecurityManager securityManager;
|
private ActiveMQSecurityManager securityManager;
|
||||||
|
|
||||||
private final Configuration configuration;
|
private final Configuration configuration;
|
||||||
|
|
||||||
|
@ -869,6 +870,19 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
||||||
this.mbeanServer = mbeanServer;
|
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) {
|
private void validateAddExternalComponent(ActiveMQComponent externalComponent) {
|
||||||
final SERVER_STATE state = this.state;
|
final SERVER_STATE state = this.state;
|
||||||
if (state == SERVER_STATE.STOPPED || state == SERVER_STATE.STOPPING) {
|
if (state == SERVER_STATE.STOPPED || state == SERVER_STATE.STOPPING) {
|
||||||
|
@ -3048,6 +3062,10 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
||||||
|
|
||||||
removeExtraAddressStores();
|
removeExtraAddressStores();
|
||||||
|
|
||||||
|
if (securityManager instanceof ActiveMQBasicSecurityManager) {
|
||||||
|
((ActiveMQBasicSecurityManager)securityManager).completeInit(storageManager);
|
||||||
|
}
|
||||||
|
|
||||||
final ServerInfo dumper = new ServerInfo(this, pagingManager);
|
final ServerInfo dumper = new ServerInfo(this, pagingManager);
|
||||||
|
|
||||||
long dumpInfoInterval = configuration.getServerDumpInterval();
|
long dumpInfoInterval = configuration.getServerDumpInterval();
|
||||||
|
@ -3381,6 +3399,8 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO load users/roles
|
||||||
|
|
||||||
journalLoader.cleanUp();
|
journalLoader.cleanUp();
|
||||||
|
|
||||||
return journalInfo;
|
return journalInfo;
|
||||||
|
@ -3395,9 +3415,9 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
||||||
addressSettingsRepository.addMatch(set.getAddressMatch().toString(), set.getSetting());
|
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());
|
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);
|
securityRepository.addMatch(roleItem.getAddressMatch().toString(), setRoles);
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.config.JMXConnectorConfiguration;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQComponent;
|
import org.apache.activemq.artemis.core.server.ActiveMQComponent;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
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.MBeanServer;
|
||||||
import javax.management.ObjectName;
|
import javax.management.ObjectName;
|
||||||
|
import javax.management.remote.JMXAuthenticator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -32,9 +35,11 @@ public class ManagementConnector implements ActiveMQComponent {
|
||||||
private ConnectorServerFactory connectorServerFactory;
|
private ConnectorServerFactory connectorServerFactory;
|
||||||
private RmiRegistryFactory rmiRegistryFactory;
|
private RmiRegistryFactory rmiRegistryFactory;
|
||||||
private MBeanServerFactory mbeanServerFactory;
|
private MBeanServerFactory mbeanServerFactory;
|
||||||
|
private ActiveMQSecurityManager securityManager;
|
||||||
|
|
||||||
public ManagementConnector(JMXConnectorConfiguration configuration) {
|
public ManagementConnector(JMXConnectorConfiguration configuration, ActiveMQSecurityManager securityManager) {
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
|
this.securityManager = securityManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -54,8 +59,15 @@ public class ManagementConnector implements ActiveMQComponent {
|
||||||
|
|
||||||
MBeanServer mbeanServer = mbeanServerFactory.getServer();
|
MBeanServer mbeanServer = mbeanServerFactory.getServer();
|
||||||
|
|
||||||
JaasAuthenticator jaasAuthenticator = new JaasAuthenticator();
|
JMXAuthenticator authenticator;
|
||||||
jaasAuthenticator.setRealm(configuration.getJmxRealm());
|
|
||||||
|
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 = new ConnectorServerFactory();
|
||||||
connectorServerFactory.setServer(mbeanServer);
|
connectorServerFactory.setServer(mbeanServer);
|
||||||
|
@ -63,7 +75,7 @@ public class ManagementConnector implements ActiveMQComponent {
|
||||||
connectorServerFactory.setRmiServerHost(configuration.getConnectorHost());
|
connectorServerFactory.setRmiServerHost(configuration.getConnectorHost());
|
||||||
connectorServerFactory.setObjectName(new ObjectName(configuration.getObjectName()));
|
connectorServerFactory.setObjectName(new ObjectName(configuration.getObjectName()));
|
||||||
Map<String, Object> environment = new HashMap<>();
|
Map<String, Object> environment = new HashMap<>();
|
||||||
environment.put("jmx.remote.authenticator", jaasAuthenticator);
|
environment.put("jmx.remote.authenticator", authenticator);
|
||||||
try {
|
try {
|
||||||
connectorServerFactory.setEnvironment(environment);
|
connectorServerFactory.setEnvironment(environment);
|
||||||
connectorServerFactory.setAuthenticatorType(configuration.getAuthenticatorType());
|
connectorServerFactory.setAuthenticatorType(configuration.getAuthenticatorType());
|
||||||
|
@ -108,4 +120,7 @@ public class ManagementConnector implements ActiveMQComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConnectorServerFactory getConnectorServerFactory() {
|
||||||
|
return connectorServerFactory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.activemq.artemis.core.server.ServiceComponent;
|
||||||
import org.apache.activemq.artemis.core.server.management.impl.HawtioSecurityControlImpl;
|
import org.apache.activemq.artemis.core.server.management.impl.HawtioSecurityControlImpl;
|
||||||
|
|
||||||
import javax.management.NotCompliantMBeanException;
|
import javax.management.NotCompliantMBeanException;
|
||||||
|
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
|
||||||
|
|
||||||
public class ManagementContext implements ServiceComponent {
|
public class ManagementContext implements ServiceComponent {
|
||||||
private AtomicBoolean isStarted = new AtomicBoolean(false);
|
private AtomicBoolean isStarted = new AtomicBoolean(false);
|
||||||
|
@ -32,6 +33,7 @@ public class ManagementContext implements ServiceComponent {
|
||||||
private JMXConnectorConfiguration jmxConnectorConfiguration;
|
private JMXConnectorConfiguration jmxConnectorConfiguration;
|
||||||
private ManagementConnector mBeanServer;
|
private ManagementConnector mBeanServer;
|
||||||
private ArtemisMBeanServerGuard guardHandler;
|
private ArtemisMBeanServerGuard guardHandler;
|
||||||
|
private ActiveMQSecurityManager securityManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start() throws Exception {
|
public void start() throws Exception {
|
||||||
|
@ -44,7 +46,7 @@ public class ManagementContext implements ServiceComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jmxConnectorConfiguration != null) {
|
if (jmxConnectorConfiguration != null) {
|
||||||
mBeanServer = new ManagementConnector(jmxConnectorConfiguration);
|
mBeanServer = new ManagementConnector(jmxConnectorConfiguration, securityManager);
|
||||||
mBeanServer.start();
|
mBeanServer.start();
|
||||||
}
|
}
|
||||||
isStarted.set(true);
|
isStarted.set(true);
|
||||||
|
@ -99,4 +101,16 @@ public class ManagementContext implements ServiceComponent {
|
||||||
public ArtemisMBeanServerGuard getArtemisMBeanServerGuard() {
|
public ArtemisMBeanServerGuard getArtemisMBeanServerGuard() {
|
||||||
return guardHandler;
|
return guardHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSecurityManager(ActiveMQSecurityManager securityManager) {
|
||||||
|
this.securityManager = securityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ActiveMQSecurityManager getSecurityManager() {
|
||||||
|
return securityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManagementConnector getManagementConnector() {
|
||||||
|
return mBeanServer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,21 +19,16 @@ package org.apache.activemq.artemis.spi.core.security;
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
import javax.security.auth.login.LoginContext;
|
import javax.security.auth.login.LoginContext;
|
||||||
import javax.security.auth.login.LoginException;
|
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 java.util.Set;
|
||||||
|
|
||||||
import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
|
import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
|
||||||
import org.apache.activemq.artemis.core.security.CheckType;
|
import org.apache.activemq.artemis.core.security.CheckType;
|
||||||
import org.apache.activemq.artemis.core.security.Role;
|
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.logs.AuditLogger;
|
||||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
|
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.JaasCallbackHandler;
|
||||||
import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
|
import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
|
||||||
|
import org.apache.activemq.artemis.utils.SecurityManagerUtil;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getCertsFromConnection;
|
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 Logger logger = Logger.getLogger(ActiveMQJAASSecurityManager.class);
|
||||||
|
|
||||||
private static final String WILDCARD = "*";
|
|
||||||
|
|
||||||
private String configurationName;
|
private String configurationName;
|
||||||
private String certificateConfigurationName;
|
private String certificateConfigurationName;
|
||||||
private SecurityConfiguration configuration;
|
private SecurityConfiguration configuration;
|
||||||
|
@ -115,33 +108,10 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
|
||||||
final Set<Role> roles,
|
final Set<Role> roles,
|
||||||
final CheckType checkType,
|
final CheckType checkType,
|
||||||
final String address) {
|
final String address) {
|
||||||
boolean authorized = false;
|
boolean authorized = SecurityManagerUtil.authorize(subject, roles, checkType, rolePrincipalClass);
|
||||||
|
|
||||||
if (subject != null) {
|
if (logger.isTraceEnabled()) {
|
||||||
Set<RolePrincipal> rolesWithPermission = getPrincipalsInRole(checkType, roles);
|
logger.trace("user " + (authorized ? " is " : " is NOT ") + "authorized");
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
|
||||||
logger.trace("user " + (authorized ? " is " : " is NOT ") + "authorized");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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) {
|
public void setConfigurationName(final String configurationName) {
|
||||||
this.configurationName = configurationName;
|
this.configurationName = configurationName;
|
||||||
}
|
}
|
||||||
|
@ -240,60 +196,4 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
|
||||||
public void setRolePrincipalClass(String rolePrincipalClass) {
|
public void setRolePrincipalClass(String rolePrincipalClass) {
|
||||||
this.rolePrincipalClass = 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -148,7 +148,7 @@ public class LDAPLoginModule implements AuditLoginModule {
|
||||||
|
|
||||||
private String getPlainPassword(String password) {
|
private String getPlainPassword(String password) {
|
||||||
try {
|
try {
|
||||||
return PasswordMaskingUtil.resolveMask(null, password, codecClass);
|
return PasswordMaskingUtil.resolveMask(password, codecClass);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IllegalArgumentException("Failed to decode password", e);
|
throw new IllegalArgumentException("Failed to decode password", e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.util.Set;
|
||||||
|
|
||||||
import org.apache.activemq.artemis.api.core.Pair;
|
import org.apache.activemq.artemis.api.core.Pair;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
|
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.activemq.artemis.utils.StringUtil;
|
||||||
import org.apache.commons.configuration2.PropertiesConfiguration;
|
import org.apache.commons.configuration2.PropertiesConfiguration;
|
||||||
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
|
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.ROLE_FILE_PROP_NAME;
|
||||||
import static org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule.USER_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 =
|
private static final String LICENSE_HEADER =
|
||||||
"## ---------------------------------------------------------------------------\n" +
|
"## ---------------------------------------------------------------------------\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) {
|
if (userConfig.getString(username) != null) {
|
||||||
throw ActiveMQMessageBundle.BUNDLE.userAlreadyExists(username);
|
throw ActiveMQMessageBundle.BUNDLE.userAlreadyExists(username);
|
||||||
}
|
}
|
||||||
|
@ -143,6 +145,7 @@ public class PropertiesLoginModuleConfigurator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void removeUser(String username) {
|
public void removeUser(String username) {
|
||||||
if (userConfig.getProperty(username) == null) {
|
if (userConfig.getProperty(username) == null) {
|
||||||
throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
|
throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
|
||||||
|
@ -151,6 +154,7 @@ public class PropertiesLoginModuleConfigurator {
|
||||||
removeRoles(username);
|
removeRoles(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Map<String, Set<String>> listUser(String username) {
|
public Map<String, Set<String>> listUser(String username) {
|
||||||
Map<String, Set<String>> result = new HashMap<>();
|
Map<String, Set<String>> result = new HashMap<>();
|
||||||
|
|
||||||
|
@ -166,7 +170,8 @@ public class PropertiesLoginModuleConfigurator {
|
||||||
return result;
|
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);
|
String oldPassword = (String) userConfig.getProperty(username);
|
||||||
if (oldPassword == null) {
|
if (oldPassword == null) {
|
||||||
throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
|
throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.StorageManager;
|
||||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
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.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.PageCountPending;
|
||||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||||
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
||||||
|
@ -597,17 +599,17 @@ public class TransactionImplTest extends ActiveMQTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
|
public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
|
public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PersistedRoles> recoverPersistedRoles() throws Exception {
|
public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,6 +628,36 @@ public class TransactionImplTest extends ActiveMQTestBase {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Override
|
||||||
public long storePageCounter(long txID, long queueID, long value, long size) throws Exception {
|
public long storePageCounter(long txID, long queueID, long value, long size) throws Exception {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -352,16 +352,23 @@ the Transport](configuring-transports.md).
|
||||||
|
|
||||||
## User credentials
|
## User credentials
|
||||||
|
|
||||||
Apache ActiveMQ Artemis ships with two security manager implementations:
|
Apache ActiveMQ Artemis ships with three 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`.
|
|
||||||
|
|
||||||
- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any
|
- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any
|
||||||
standard JAAS login module. Artemis ships with several login modules which
|
standard JAAS login module. Artemis ships with several login modules which
|
||||||
will be discussed further down. This is the default security manager.
|
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
|
### JAAS Security Manager
|
||||||
|
|
||||||
When using the Java Authentication and Authorization Service (JAAS) much of the
|
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*
|
client), using TLS can provide Kerberos authentication over an *unsecure*
|
||||||
channel.
|
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
|
## 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:
|
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:
|
||||||
|
|
||||||
|
|
|
@ -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.StorageManager;
|
||||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
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.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.PageCountPending;
|
||||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||||
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
||||||
|
@ -694,18 +696,18 @@ public class SendAckFailTest extends SpawnedTestBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
|
public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
|
||||||
manager.storeSecurityRoles(persistedRoles);
|
manager.storeSecuritySetting(persistedRoles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
|
public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
|
||||||
manager.deleteSecurityRoles(addressMatch);
|
manager.deleteSecuritySetting(addressMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PersistedRoles> recoverPersistedRoles() throws Exception {
|
public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
|
||||||
return manager.recoverPersistedRoles();
|
return manager.recoverSecuritySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -723,6 +725,36 @@ public class SendAckFailTest extends SpawnedTestBase {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Override
|
||||||
public long storePageCounter(long txID, long queueID, long value, long size) throws Exception {
|
public long storePageCounter(long txID, long queueID, long value, long size) throws Exception {
|
||||||
return manager.storePageCounter(txID, queueID, value, size);
|
return manager.storePageCounter(txID, queueID, value, size);
|
||||||
|
|
|
@ -22,13 +22,13 @@ import java.util.Map;
|
||||||
|
|
||||||
import org.apache.activemq.artemis.api.core.SimpleString;
|
import org.apache.activemq.artemis.api.core.SimpleString;
|
||||||
import org.apache.activemq.artemis.core.config.StoreConfiguration;
|
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.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
||||||
|
|
||||||
private Map<SimpleString, PersistedRoles> mapExpectedSets;
|
private Map<SimpleString, PersistedSecuritySetting> mapExpectedSets;
|
||||||
|
|
||||||
public RolesConfigurationStorageTest(StoreConfiguration.StoreType storeType) {
|
public RolesConfigurationStorageTest(StoreConfiguration.StoreType storeType) {
|
||||||
super(storeType);
|
super(storeType);
|
||||||
|
@ -41,18 +41,18 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
||||||
mapExpectedSets = new HashMap<>();
|
mapExpectedSets = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addSetting(PersistedRoles setting) throws Exception {
|
protected void addSetting(PersistedSecuritySetting setting) throws Exception {
|
||||||
mapExpectedSets.put(setting.getAddressMatch(), setting);
|
mapExpectedSets.put(setting.getAddressMatch(), setting);
|
||||||
journal.storeSecurityRoles(setting);
|
journal.storeSecuritySetting(setting);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStoreSecuritySettings() throws Exception {
|
public void testStoreSecuritySettings() throws Exception {
|
||||||
createStorage();
|
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();
|
journal.stop();
|
||||||
|
|
||||||
|
@ -62,9 +62,9 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
||||||
|
|
||||||
checkSettings();
|
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();
|
checkSettings();
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
||||||
|
|
||||||
checkSettings();
|
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();
|
journal.stop();
|
||||||
|
|
||||||
|
@ -112,12 +112,12 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private void checkSettings() throws Exception {
|
private void checkSettings() throws Exception {
|
||||||
List<PersistedRoles> listSetting = journal.recoverPersistedRoles();
|
List<PersistedSecuritySetting> listSetting = journal.recoverSecuritySettings();
|
||||||
|
|
||||||
assertEquals(mapExpectedSets.size(), listSetting.size());
|
assertEquals(mapExpectedSets.size(), listSetting.size());
|
||||||
|
|
||||||
for (PersistedRoles el : listSetting) {
|
for (PersistedSecuritySetting el : listSetting) {
|
||||||
PersistedRoles el2 = mapExpectedSets.get(el.getAddressMatch());
|
PersistedSecuritySetting el2 = mapExpectedSets.get(el.getAddressMatch());
|
||||||
|
|
||||||
assertEquals(el, el2);
|
assertEquals(el, el2);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue