This closes #3300
This commit is contained in:
commit
0be78c84c5
|
@ -107,6 +107,8 @@ public class Create extends InputAbstract {
|
|||
public static final String ETC_PING_TXT = "etc/ping-settings.txt";
|
||||
public static final String ETC_COMMENTED_PING_TXT = "etc/commented-ping-settings.txt";
|
||||
public static final String ETC_DATABASE_STORE_TXT = "etc/database-store.txt";
|
||||
public static final String ETC_JAAS_SECURITY_MANAGER_TXT = "etc/jaas-security-manager.txt";
|
||||
public static final String ETC_BASIC_SECURITY_MANAGER_TXT = "etc/basic-security-manager.txt";
|
||||
|
||||
public static final String ETC_GLOBAL_MAX_SPECIFIED_TXT = "etc/global-max-specified.txt";
|
||||
public static final String ETC_GLOBAL_MAX_DEFAULT_TXT = "etc/global-max-default.txt";
|
||||
|
@ -289,6 +291,9 @@ public class Create extends InputAbstract {
|
|||
}
|
||||
}
|
||||
|
||||
@Option(name = "--security-manager", description = "Which security manager to use - jaas or basic (Default: jaas)")
|
||||
private String securityManager = "jaas";
|
||||
|
||||
@Option(name = "--jdbc-bindings-table-name", description = "Name of the jdbc bindigns table")
|
||||
private String jdbcBindings = ActiveMQDefaultConfiguration.getDefaultBindingsTableName();
|
||||
|
||||
|
@ -821,6 +826,11 @@ public class Create extends InputAbstract {
|
|||
|
||||
// we want this variable to remain unchanged so that it will use the value set in the profile
|
||||
filters.remove("${artemis.instance}");
|
||||
if (SecurityManagerType.getType(securityManager) == SecurityManagerType.BASIC) {
|
||||
filters.put("${security-manager-settings}", readTextFile(ETC_BASIC_SECURITY_MANAGER_TXT, filters));
|
||||
} else {
|
||||
filters.put("${security-manager-settings}", readTextFile(ETC_JAAS_SECURITY_MANAGER_TXT, filters));
|
||||
}
|
||||
writeEtc(ETC_BOOTSTRAP_XML, etcFolder, filters, false);
|
||||
writeEtc(ETC_MANAGEMENT_XML, etcFolder, filters, false);
|
||||
|
||||
|
@ -1193,4 +1203,20 @@ public class Create extends InputAbstract {
|
|||
c = is.read(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private enum SecurityManagerType {
|
||||
JAAS, BASIC;
|
||||
|
||||
public static SecurityManagerType getType(String type) {
|
||||
type = type.toLowerCase();
|
||||
switch (type) {
|
||||
case "jaas":
|
||||
return JAAS;
|
||||
case "basic":
|
||||
return BASIC;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,17 +72,15 @@ public class Run extends LockAbstract {
|
|||
super.execute(context);
|
||||
|
||||
try {
|
||||
BrokerDTO broker = getBrokerDTO();
|
||||
ActiveMQSecurityManager securityManager = SecurityManagerFactory.create(broker.security);
|
||||
ManagementContextDTO managementDTO = getManagementDTO();
|
||||
managementContext = ManagementFactory.create(managementDTO);
|
||||
managementContext = ManagementFactory.create(managementDTO, securityManager);
|
||||
|
||||
Artemis.printBanner();
|
||||
|
||||
BrokerDTO broker = getBrokerDTO();
|
||||
|
||||
addShutdownHook(broker.server.getConfigurationFile().getParentFile());
|
||||
|
||||
ActiveMQSecurityManager security = SecurityManagerFactory.create(broker.security);
|
||||
|
||||
ActivateCallback activateCallback = new ActivateCallback() {
|
||||
@Override
|
||||
public void preActivate() {
|
||||
|
@ -109,7 +107,7 @@ public class Run extends LockAbstract {
|
|||
}
|
||||
};
|
||||
|
||||
server = BrokerFactory.createServer(broker.server, security, activateCallback);
|
||||
server = BrokerFactory.createServer(broker.server, securityManager, activateCallback);
|
||||
|
||||
managementContext.start();
|
||||
server.createComponents();
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.activemq.artemis.dto.JMXConnectorDTO;
|
|||
import org.apache.activemq.artemis.dto.ManagementContextDTO;
|
||||
import org.apache.activemq.artemis.dto.MatchDTO;
|
||||
import org.apache.activemq.artemis.core.server.management.JMXAccessControlList;
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
|
||||
import org.apache.activemq.artemis.utils.FactoryFinder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -60,7 +61,7 @@ public class ManagementFactory {
|
|||
return createJmxAclConfiguration(new URI(configuration), artemisHome, artemisInstance, artemisURIInstance);
|
||||
}
|
||||
|
||||
public static ManagementContext create(ManagementContextDTO config) throws Exception {
|
||||
public static ManagementContext create(ManagementContextDTO config, ActiveMQSecurityManager securityManager) throws Exception {
|
||||
ManagementContext context = new ManagementContext();
|
||||
|
||||
if (config.getAuthorisation() != null) {
|
||||
|
@ -130,6 +131,7 @@ public class ManagementFactory {
|
|||
jmxConnectorConfiguration.setSecured(jmxConnector.isSecured());
|
||||
}
|
||||
context.setJmxConnectorConfiguration(jmxConnectorConfiguration);
|
||||
context.setSecurityManager(securityManager);
|
||||
}
|
||||
|
||||
return context;
|
||||
|
|
|
@ -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">
|
||||
|
||||
<jaas-security domain="activemq"/>
|
||||
${security-manager-settings}
|
||||
|
||||
<!-- artemis.URI.instance is parsed from artemis.instance by the CLI startup.
|
||||
This is to avoid situations where you could have spaces or special characters on this URI -->
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<jaas-security domain="activemq"/>
|
|
@ -105,6 +105,9 @@ import static org.junit.Assert.fail;
|
|||
public class ArtemisTest extends CliTestBase {
|
||||
private static final Logger log = Logger.getLogger(ArtemisTest.class);
|
||||
|
||||
// some tests will set this, as some security methods will need to know the server the CLI started
|
||||
private ActiveMQServer server;
|
||||
|
||||
@Before
|
||||
@Override
|
||||
public void setup() throws Exception {
|
||||
|
@ -340,13 +343,23 @@ public class ArtemisTest extends CliTestBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUserCommand() throws Exception {
|
||||
public void testUserCommandJAAS() throws Exception {
|
||||
testUserCommand(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserCommandBasic() throws Exception {
|
||||
testUserCommand(true);
|
||||
}
|
||||
|
||||
private void testUserCommand(boolean basic) throws Exception {
|
||||
Run.setEmbedded(true);
|
||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login", "--security-manager", basic ? "basic" : "jaas");
|
||||
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||
Artemis.internalExecute("run");
|
||||
Object runResult = Artemis.internalExecute("run");
|
||||
server = ((Pair<ManagementContext, ActiveMQServer>)runResult).getB();
|
||||
|
||||
try {
|
||||
File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
|
||||
|
@ -363,7 +376,7 @@ public class ArtemisTest extends CliTestBase {
|
|||
|
||||
//default only one user admin with role amq
|
||||
assertTrue(result.contains("\"admin\"(amq)"));
|
||||
checkRole("admin", roleFile, "amq");
|
||||
checkRole("admin", roleFile, basic, "amq");
|
||||
|
||||
//add a simple user
|
||||
AddUser addCmd = new AddUser();
|
||||
|
@ -383,8 +396,8 @@ public class ArtemisTest extends CliTestBase {
|
|||
assertTrue(result.contains("\"admin\"(amq)"));
|
||||
assertTrue(result.contains("\"guest\"(admin)"));
|
||||
|
||||
checkRole("guest", roleFile, "admin");
|
||||
assertTrue(checkPassword("guest", "guest123", userFile));
|
||||
checkRole("guest", roleFile, basic, "admin");
|
||||
assertTrue(checkPassword("guest", "guest123", userFile, basic));
|
||||
|
||||
//add a user with 2 roles
|
||||
addCmd = new AddUser();
|
||||
|
@ -405,8 +418,8 @@ public class ArtemisTest extends CliTestBase {
|
|||
assertTrue(result.contains("\"guest\"(admin)"));
|
||||
assertTrue(result.contains("\"scott\"(admin,operator)"));
|
||||
|
||||
checkRole("scott", roleFile, "admin", "operator");
|
||||
assertTrue(checkPassword("scott", "tiger", userFile));
|
||||
checkRole("scott", roleFile, basic, "admin", "operator");
|
||||
assertTrue(checkPassword("scott", "tiger", userFile, basic));
|
||||
|
||||
//add an existing user
|
||||
addCmd = new AddUser();
|
||||
|
@ -504,24 +517,34 @@ public class ArtemisTest extends CliTestBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUserCommandViaManagementPlaintext() throws Exception {
|
||||
internalTestUserCommandViaManagement(true);
|
||||
public void testUserCommandViaManagementPlaintextJAAS() throws Exception {
|
||||
internalTestUserCommandViaManagement(true, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserCommandViaManagementHashed() throws Exception {
|
||||
internalTestUserCommandViaManagement(false);
|
||||
public void testUserCommandViaManagementHashedJAAS() throws Exception {
|
||||
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);
|
||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor", "--security-manager", basic ? "basic" : "jaas");
|
||||
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||
Object result = Artemis.internalExecute("run");
|
||||
ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
|
||||
ActiveMQServerControl activeMQServerControl = activeMQServer.getActiveMQServerControl();
|
||||
server = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
|
||||
ActiveMQServerControl activeMQServerControl = server.getActiveMQServerControl();
|
||||
|
||||
File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
|
||||
File roleFile = new File(instance1.getAbsolutePath() + "/etc/artemis-roles.properties");
|
||||
|
@ -529,7 +552,7 @@ public class ArtemisTest extends CliTestBase {
|
|||
//default only one user admin with role amq
|
||||
String jsonResult = activeMQServerControl.listUser("");
|
||||
contains(JsonUtil.readJsonArray(jsonResult), "admin", "amq");
|
||||
checkRole("admin", roleFile, "amq");
|
||||
checkRole("admin", roleFile, basic, "amq");
|
||||
|
||||
//add a simple user
|
||||
activeMQServerControl.addUser("guest", "guest123", "admin", plaintext);
|
||||
|
@ -537,9 +560,9 @@ public class ArtemisTest extends CliTestBase {
|
|||
//verify add
|
||||
jsonResult = activeMQServerControl.listUser("");
|
||||
contains(JsonUtil.readJsonArray(jsonResult), "guest", "admin");
|
||||
checkRole("guest", roleFile, "admin");
|
||||
assertTrue(checkPassword("guest", "guest123", userFile));
|
||||
assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("guest", userFile)));
|
||||
checkRole("guest", roleFile, basic, "admin");
|
||||
assertTrue(checkPassword("guest", "guest123", userFile, basic));
|
||||
assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("guest", userFile, basic)));
|
||||
|
||||
//add a user with 2 roles
|
||||
activeMQServerControl.addUser("scott", "tiger", "admin,operator", plaintext);
|
||||
|
@ -548,9 +571,9 @@ public class ArtemisTest extends CliTestBase {
|
|||
jsonResult = activeMQServerControl.listUser("");
|
||||
contains(JsonUtil.readJsonArray(jsonResult), "scott", "admin");
|
||||
contains(JsonUtil.readJsonArray(jsonResult), "scott", "operator");
|
||||
checkRole("scott", roleFile, "admin", "operator");
|
||||
assertTrue(checkPassword("scott", "tiger", userFile));
|
||||
assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("scott", userFile)));
|
||||
checkRole("scott", roleFile, basic, "admin", "operator");
|
||||
assertTrue(checkPassword("scott", "tiger", userFile, basic));
|
||||
assertEquals(plaintext, !PasswordMaskingUtil.isEncMasked(getStoredPassword("scott", userFile, basic)));
|
||||
|
||||
try {
|
||||
activeMQServerControl.addUser("scott", "password", "visitor", plaintext);
|
||||
|
@ -615,11 +638,20 @@ public class ArtemisTest extends CliTestBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testProperReloadWhenAddingUserViaManagement() throws Exception {
|
||||
public void testProperReloadWhenAddingUserViaManagementJAAS() throws Exception {
|
||||
testProperReloadWhenAddingUserViaManagement(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProperReloadWhenAddingUserViaManagementBasic() throws Exception {
|
||||
testProperReloadWhenAddingUserViaManagement(true);
|
||||
}
|
||||
|
||||
private void testProperReloadWhenAddingUserViaManagement(boolean basic) throws Exception {
|
||||
Run.setEmbedded(true);
|
||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor", "--require-login");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--no-amqp-acceptor", "--no-mqtt-acceptor", "--no-stomp-acceptor", "--no-hornetq-acceptor", "--require-login", "--security-manager", basic ? "basic" : "jaas");
|
||||
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||
Object result = Artemis.internalExecute("run");
|
||||
ActiveMQServer activeMQServer = ((Pair<ManagementContext, ActiveMQServer>)result).getB();
|
||||
|
@ -702,13 +734,23 @@ public class ArtemisTest extends CliTestBase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testUserCommandReset() throws Exception {
|
||||
public void testUserCommandResetJAAS() throws Exception {
|
||||
testUserCommandReset(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserCommandResetBasic() throws Exception {
|
||||
testUserCommandReset(true);
|
||||
}
|
||||
|
||||
private void testUserCommandReset(boolean basic) throws Exception {
|
||||
Run.setEmbedded(true);
|
||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login", "--security-manager", basic ? "basic" : "jaas");
|
||||
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||
Artemis.internalExecute("run");
|
||||
Object runResult = Artemis.internalExecute("run");
|
||||
server = ((Pair<ManagementContext, ActiveMQServer>)runResult).getB();
|
||||
|
||||
try {
|
||||
File userFile = new File(instance1.getAbsolutePath() + "/etc/artemis-users.properties");
|
||||
|
@ -754,7 +796,7 @@ public class ArtemisTest extends CliTestBase {
|
|||
addCmd.setUserCommandPassword("password1");
|
||||
addCmd.setRole("admin,manager");
|
||||
addCmd.execute(new TestActionContext());
|
||||
assertTrue(checkPassword("user1", "password1", userFile));
|
||||
assertTrue(checkPassword("user1", "password1", userFile, basic));
|
||||
|
||||
addCmd.setUserCommandUser("user2");
|
||||
addCmd.setUserCommandPassword("password2");
|
||||
|
@ -787,7 +829,7 @@ public class ArtemisTest extends CliTestBase {
|
|||
.matcher(result)
|
||||
.find());
|
||||
|
||||
checkRole("user1", roleFile, "admin", "manager");
|
||||
checkRole("user1", roleFile, basic, "admin", "manager");
|
||||
|
||||
//reset password
|
||||
context = new TestActionContext();
|
||||
|
@ -798,16 +840,16 @@ public class ArtemisTest extends CliTestBase {
|
|||
resetCommand.setPassword("admin");
|
||||
resetCommand.execute(context);
|
||||
|
||||
checkRole("user1", roleFile, "admin", "manager");
|
||||
assertFalse(checkPassword("user1", "password1", userFile));
|
||||
assertTrue(checkPassword("user1", "newpassword1", userFile));
|
||||
checkRole("user1", roleFile, basic, "admin", "manager");
|
||||
assertFalse(checkPassword("user1", "password1", userFile, basic));
|
||||
assertTrue(checkPassword("user1", "newpassword1", userFile, basic));
|
||||
|
||||
//reset role
|
||||
resetCommand.setUserCommandUser("user2");
|
||||
resetCommand.setRole("manager,master,operator");
|
||||
resetCommand.execute(new TestActionContext());
|
||||
|
||||
checkRole("user2", roleFile, "manager", "master", "operator");
|
||||
checkRole("user2", roleFile, basic, "manager", "master", "operator");
|
||||
|
||||
//reset both
|
||||
resetCommand.setUserCommandUser("user3");
|
||||
|
@ -815,19 +857,28 @@ public class ArtemisTest extends CliTestBase {
|
|||
resetCommand.setRole("admin,system");
|
||||
resetCommand.execute(new ActionContext());
|
||||
|
||||
checkRole("user3", roleFile, "admin", "system");
|
||||
assertTrue(checkPassword("user3", "newpassword3", userFile));
|
||||
checkRole("user3", roleFile, basic, "admin", "system");
|
||||
assertTrue(checkPassword("user3", "newpassword3", userFile, basic));
|
||||
} finally {
|
||||
stopServer();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcurrentUserAdministration() throws Exception {
|
||||
public void testConcurrentUserAdministrationJAAS() throws Exception {
|
||||
testConcurrentUserAdministration(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConcurrentUserAdministrationBasic() throws Exception {
|
||||
testConcurrentUserAdministration(true);
|
||||
}
|
||||
|
||||
private void testConcurrentUserAdministration(boolean basic) throws Exception {
|
||||
Run.setEmbedded(true);
|
||||
File instance1 = new File(temporaryFolder.getRoot(), "instance_user");
|
||||
System.setProperty("java.security.auth.login.config", instance1.getAbsolutePath() + "/etc/login.config");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login");
|
||||
Artemis.main("create", instance1.getAbsolutePath(), "--silent", "--no-autotune", "--no-web", "--require-login", "--security-manager", basic ? "basic" : "jaas");
|
||||
System.setProperty("artemis.instance", instance1.getAbsolutePath());
|
||||
Artemis.internalExecute("run");
|
||||
|
||||
|
@ -1711,28 +1762,52 @@ public class ArtemisTest extends CliTestBase {
|
|||
}
|
||||
|
||||
private void checkRole(String user, File roleFile, String... roles) throws Exception {
|
||||
Configurations configs = new Configurations();
|
||||
FileBasedConfigurationBuilder<PropertiesConfiguration> roleBuilder = configs.propertiesBuilder(roleFile);
|
||||
PropertiesConfiguration roleConfig = roleBuilder.getConfiguration();
|
||||
checkRole(user, roleFile, false, roles);
|
||||
}
|
||||
|
||||
for (String r : roles) {
|
||||
String storedUsers = (String) roleConfig.getProperty(r);
|
||||
private void checkRole(String user, File roleFile, boolean basicSecurityManager, String... roles) throws Exception {
|
||||
if (basicSecurityManager) {
|
||||
for (String r : roles) {
|
||||
assertTrue(server.getStorageManager().getPersistedRoles().get(user).getRoles().contains(r));
|
||||
}
|
||||
} else {
|
||||
Configurations configs = new Configurations();
|
||||
FileBasedConfigurationBuilder<PropertiesConfiguration> roleBuilder = configs.propertiesBuilder(roleFile);
|
||||
PropertiesConfiguration roleConfig = roleBuilder.getConfiguration();
|
||||
|
||||
log.debug("users in role: " + r + " ; " + storedUsers);
|
||||
List<String> userList = StringUtil.splitStringList(storedUsers, ",");
|
||||
assertTrue(userList.contains(user));
|
||||
for (String r : roles) {
|
||||
String storedUsers = (String) roleConfig.getProperty(r);
|
||||
|
||||
log.debug("users in role: " + r + " ; " + storedUsers);
|
||||
List<String> userList = StringUtil.splitStringList(storedUsers, ",");
|
||||
assertTrue(userList.contains(user));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getStoredPassword(String user, File userFile) throws Exception {
|
||||
Configurations configs = new Configurations();
|
||||
FileBasedConfigurationBuilder<PropertiesConfiguration> userBuilder = configs.propertiesBuilder(userFile);
|
||||
PropertiesConfiguration userConfig = userBuilder.getConfiguration();
|
||||
return (String) userConfig.getProperty(user);
|
||||
return getStoredPassword(user, userFile, false);
|
||||
}
|
||||
|
||||
private String getStoredPassword(String user, File userFile, boolean basicSecurityManager) throws Exception {
|
||||
String result;
|
||||
if (basicSecurityManager) {
|
||||
result = server.getStorageManager().getPersistedUsers().get(user).getPassword();
|
||||
} else {
|
||||
Configurations configs = new Configurations();
|
||||
FileBasedConfigurationBuilder<PropertiesConfiguration> userBuilder = configs.propertiesBuilder(userFile);
|
||||
PropertiesConfiguration userConfig = userBuilder.getConfiguration();
|
||||
result = (String) userConfig.getProperty(user);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean checkPassword(String user, String password, File userFile) throws Exception {
|
||||
String storedPassword = getStoredPassword(user, userFile);
|
||||
return checkPassword(user, password, userFile,false);
|
||||
}
|
||||
|
||||
private boolean checkPassword(String user, String password, File userFile, boolean basicSecurityManager) throws Exception {
|
||||
String storedPassword = getStoredPassword(user, userFile, basicSecurityManager);
|
||||
HashProcessor processor = PasswordMaskingUtil.getHashProcessor(storedPassword);
|
||||
return processor.compare(password.toCharArray(), storedPassword);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
* @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
|
||||
public static HashProcessor getHashProcessor(String storedPassword) throws Exception {
|
||||
public static HashProcessor getHashProcessor(String storedPassword) {
|
||||
|
||||
if (!isEncoded(storedPassword)) {
|
||||
return LazyPlainTextProcessorHolder.INSTANCE;
|
||||
|
|
|
@ -1830,45 +1830,45 @@ public interface ActiveMQServerControl {
|
|||
* @param roles
|
||||
* @throws Exception
|
||||
*/
|
||||
@Operation(desc = "add a user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
|
||||
@Operation(desc = "add a user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
|
||||
void addUser(@Parameter(name = "username", desc = "Name of the user") String username,
|
||||
@Parameter(name = "password", desc = "User's password") String password,
|
||||
@Parameter(name = "roles", desc = "User's role (comma separated)") String roles,
|
||||
@Parameter(name = "plaintext", desc = "whether or not to store the password in plaintext or hash it") boolean plaintext) throws Exception;
|
||||
|
||||
/**
|
||||
* List the information about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule).
|
||||
* List the information about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager).
|
||||
*
|
||||
* @param username
|
||||
* @return JSON array of user and role information
|
||||
* @throws Exception
|
||||
*/
|
||||
@Operation(desc = "list info about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
|
||||
@Operation(desc = "list info about a user or all users if no username is supplied (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
|
||||
String listUser(@Parameter(name = "username", desc = "Name of the user; leave null to list all known users") String username) throws Exception;
|
||||
|
||||
/**
|
||||
* Remove a user (only applicable when using the JAAS PropertiesLoginModule).
|
||||
* Remove a user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager).
|
||||
*
|
||||
* @param username
|
||||
* @throws Exception
|
||||
*/
|
||||
@Operation(desc = "remove a user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
|
||||
@Operation(desc = "remove a user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
|
||||
void removeUser(@Parameter(name = "username", desc = "Name of the user") String username) throws Exception;
|
||||
|
||||
/**
|
||||
* Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule).
|
||||
* Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager).
|
||||
*
|
||||
* @param username
|
||||
* @param password
|
||||
* @param roles
|
||||
* @throws Exception
|
||||
*/
|
||||
@Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
|
||||
@Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
|
||||
void resetUser(@Parameter(name = "username", desc = "Name of the user") String username,
|
||||
@Parameter(name = "password", desc = "User's password") String password,
|
||||
@Parameter(name = "roles", desc = "User's role (comma separated)") String roles) throws Exception;
|
||||
/**
|
||||
* Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule).
|
||||
* Set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager).
|
||||
*
|
||||
* @param username
|
||||
* @param password
|
||||
|
@ -1877,7 +1877,7 @@ public interface ActiveMQServerControl {
|
|||
* @throws Exception
|
||||
*/
|
||||
|
||||
@Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule)", impact = MBeanOperationInfo.ACTION)
|
||||
@Operation(desc = "set new properties on an existing user (only applicable when using the JAAS PropertiesLoginModule or the ActiveMQBasicSecurityManager)", impact = MBeanOperationInfo.ACTION)
|
||||
void resetUser(@Parameter(name = "username", desc = "Name of the user") String username,
|
||||
@Parameter(name = "password", desc = "User's password") String password,
|
||||
@Parameter(name = "roles", desc = "User's role (comma separated)") String roles,
|
||||
|
|
|
@ -123,6 +123,6 @@ public class JMXConnectorDTO {
|
|||
}
|
||||
|
||||
private String getPassword(String password) throws Exception {
|
||||
return PasswordMaskingUtil.resolveMask(null, password, this.passwordCodec);
|
||||
return PasswordMaskingUtil.resolveMask(password, this.passwordCodec);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ public class WebServerDTO extends ComponentDTO {
|
|||
}
|
||||
|
||||
private String getPassword(String password) throws Exception {
|
||||
return PasswordMaskingUtil.resolveMask(null, password, this.passwordCodec);
|
||||
return PasswordMaskingUtil.resolveMask(password, this.passwordCodec);
|
||||
}
|
||||
|
||||
public void setKeyStorePassword(String keyStorePassword) {
|
||||
|
|
|
@ -83,7 +83,7 @@ import org.apache.activemq.artemis.core.messagecounter.impl.MessageCounterManage
|
|||
import org.apache.activemq.artemis.core.persistence.StorageManager;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
|
||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||
import org.apache.activemq.artemis.core.postoffice.Bindings;
|
||||
import org.apache.activemq.artemis.core.postoffice.DuplicateIDCache;
|
||||
|
@ -96,9 +96,9 @@ import org.apache.activemq.artemis.core.security.impl.SecurityStoreImpl;
|
|||
import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||
import org.apache.activemq.artemis.core.server.ComponentConfigurationRoutingType;
|
||||
import org.apache.activemq.artemis.core.server.ConnectorServiceFactory;
|
||||
import org.apache.activemq.artemis.core.server.Consumer;
|
||||
import org.apache.activemq.artemis.core.server.ComponentConfigurationRoutingType;
|
||||
import org.apache.activemq.artemis.core.server.Divert;
|
||||
import org.apache.activemq.artemis.core.server.JournalType;
|
||||
import org.apache.activemq.artemis.core.server.Queue;
|
||||
|
@ -127,6 +127,7 @@ import org.apache.activemq.artemis.core.transaction.impl.CoreTransactionDetail;
|
|||
import org.apache.activemq.artemis.core.transaction.impl.XidImpl;
|
||||
import org.apache.activemq.artemis.logs.AuditLogger;
|
||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModuleConfigurator;
|
||||
import org.apache.activemq.artemis.utils.JsonLoader;
|
||||
import org.apache.activemq.artemis.utils.ListUtil;
|
||||
|
@ -2784,9 +2785,9 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
|
|||
|
||||
server.getSecurityRepository().addMatch(addressMatch, roles);
|
||||
|
||||
PersistedRoles persistedRoles = new PersistedRoles(addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles, createAddressRoles, deleteAddressRoles);
|
||||
PersistedSecuritySetting persistedRoles = new PersistedSecuritySetting(addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles, createAddressRoles, deleteAddressRoles);
|
||||
|
||||
storageManager.storeSecurityRoles(persistedRoles);
|
||||
storageManager.storeSecuritySetting(persistedRoles);
|
||||
} finally {
|
||||
blockOnIO();
|
||||
}
|
||||
|
@ -2802,7 +2803,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
|
|||
clearIO();
|
||||
try {
|
||||
server.getSecurityRepository().removeMatch(addressMatch);
|
||||
storageManager.deleteSecurityRoles(new SimpleString(addressMatch));
|
||||
storageManager.deleteSecuritySetting(new SimpleString(addressMatch));
|
||||
} finally {
|
||||
blockOnIO();
|
||||
}
|
||||
|
@ -4189,15 +4190,22 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
|
|||
|
||||
@Override
|
||||
public void addUser(String username, String password, String roles, boolean plaintext) throws Exception {
|
||||
synchronized (userLock) {
|
||||
if (AuditLogger.isEnabled()) {
|
||||
AuditLogger.addUser(this.server, username, "****", roles, plaintext);
|
||||
if (AuditLogger.isEnabled()) {
|
||||
AuditLogger.addUser(this.server, username, "****", roles, plaintext);
|
||||
}
|
||||
|
||||
String passwordToUse = plaintext ? password : PasswordMaskingUtil.getHashProcessor().hash(password);
|
||||
|
||||
if (server.getSecurityManager() instanceof ActiveMQBasicSecurityManager) {
|
||||
((ActiveMQBasicSecurityManager) server.getSecurityManager()).addNewUser(username, passwordToUse, roles.split(","));
|
||||
} else {
|
||||
synchronized (userLock) {
|
||||
tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
|
||||
PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
|
||||
config.addNewUser(username, passwordToUse, roles.split(","));
|
||||
config.save();
|
||||
});
|
||||
}
|
||||
tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
|
||||
PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
|
||||
config.addNewUser(username, plaintext ? password : PasswordMaskingUtil.getHashProcessor().hash(password), roles.split(","));
|
||||
config.save();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4207,58 +4215,72 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
|
|||
|
||||
@Override
|
||||
public String listUser(String username) throws Exception {
|
||||
synchronized (userLock) {
|
||||
if (AuditLogger.isEnabled()) {
|
||||
AuditLogger.listUser(this.server, username);
|
||||
}
|
||||
|
||||
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 (AuditLogger.isEnabled()) {
|
||||
AuditLogger.listUser(this.server, username);
|
||||
}
|
||||
if (server.getSecurityManager() instanceof ActiveMQBasicSecurityManager) {
|
||||
return buildJsonUserList(((ActiveMQBasicSecurityManager) server.getSecurityManager()).listUser(username));
|
||||
} else {
|
||||
synchronized (userLock) {
|
||||
return (String) tcclCall(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
|
||||
return buildJsonUserList(getPropertiesLoginModuleConfigurator().listUser(username));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String buildJsonUserList(Map<String, Set<String>> info) {
|
||||
JsonArrayBuilder users = JsonLoader.createArrayBuilder();
|
||||
for (Entry<String, Set<String>> entry : info.entrySet()) {
|
||||
JsonObjectBuilder user = JsonLoader.createObjectBuilder();
|
||||
user.add("username", entry.getKey());
|
||||
JsonArrayBuilder roles = JsonLoader.createArrayBuilder();
|
||||
for (String role : entry.getValue()) {
|
||||
roles.add(role);
|
||||
}
|
||||
user.add("roles", roles);
|
||||
users.add(user);
|
||||
}
|
||||
return users.build().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUser(String username) throws Exception {
|
||||
synchronized (userLock) {
|
||||
if (AuditLogger.isEnabled()) {
|
||||
AuditLogger.removeUser(this.server, username);
|
||||
if (AuditLogger.isEnabled()) {
|
||||
AuditLogger.removeUser(this.server, username);
|
||||
}
|
||||
if (server.getSecurityManager() instanceof ActiveMQBasicSecurityManager) {
|
||||
((ActiveMQBasicSecurityManager) server.getSecurityManager()).removeUser(username);
|
||||
} else {
|
||||
synchronized (userLock) {
|
||||
tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
|
||||
PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
|
||||
config.removeUser(username);
|
||||
config.save();
|
||||
});
|
||||
}
|
||||
tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
|
||||
PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
|
||||
config.removeUser(username);
|
||||
config.save();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetUser(String username, String password, String roles, boolean plaintext) throws Exception {
|
||||
synchronized (userLock) {
|
||||
if (AuditLogger.isEnabled()) {
|
||||
AuditLogger.resetUser(this.server, username, "****", roles, plaintext);
|
||||
if (AuditLogger.isEnabled()) {
|
||||
AuditLogger.resetUser(this.server, username, "****", roles, plaintext);
|
||||
}
|
||||
|
||||
String passwordToUse = password == null ? password : plaintext ? password : PasswordMaskingUtil.getHashProcessor().hash(password);
|
||||
|
||||
if (server.getSecurityManager() instanceof ActiveMQBasicSecurityManager) {
|
||||
((ActiveMQBasicSecurityManager) server.getSecurityManager()).updateUser(username, passwordToUse, roles == null ? null : roles.split(","));
|
||||
} else {
|
||||
synchronized (userLock) {
|
||||
tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
|
||||
PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
|
||||
// don't hash a null password even if plaintext = false
|
||||
config.updateUser(username, passwordToUse, roles == null ? null : roles.split(","));
|
||||
config.save();
|
||||
});
|
||||
}
|
||||
tcclInvoke(ActiveMQServerControlImpl.class.getClassLoader(), () -> {
|
||||
PropertiesLoginModuleConfigurator config = getPropertiesLoginModuleConfigurator();
|
||||
// don't hash a null password even if plaintext = false
|
||||
config.updateUser(username, password == null ? password : plaintext ? password : PasswordMaskingUtil
|
||||
.getHashProcessor()
|
||||
.hash(password), roles == null ? null : roles.split(","));
|
||||
config.save();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ import org.apache.activemq.artemis.core.paging.PagingStore;
|
|||
import org.apache.activemq.artemis.core.paging.cursor.PagePosition;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
|
||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
||||
|
@ -354,17 +356,30 @@ public interface StorageManager extends IDGenerator, ActiveMQComponent {
|
|||
|
||||
List<PersistedAddressSetting> recoverAddressSettings() throws Exception;
|
||||
|
||||
void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception;
|
||||
void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception;
|
||||
|
||||
void deleteSecurityRoles(SimpleString addressMatch) throws Exception;
|
||||
void deleteSecuritySetting(SimpleString addressMatch) throws Exception;
|
||||
|
||||
List<PersistedRoles> recoverPersistedRoles() throws Exception;
|
||||
List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception;
|
||||
|
||||
void storeDivertConfiguration(PersistedDivertConfiguration persistedDivertConfiguration) throws Exception;
|
||||
|
||||
void deleteDivertConfiguration(String divertName) throws Exception;
|
||||
|
||||
List<PersistedDivertConfiguration> recoverDivertConfigurations();
|
||||
|
||||
void storeUser(PersistedUser persistedUser) throws Exception;
|
||||
|
||||
void deleteUser(String username) throws Exception;
|
||||
|
||||
Map<String, PersistedUser> getPersistedUsers();
|
||||
|
||||
void storeRole(PersistedRole persistedRole) throws Exception;
|
||||
|
||||
void deleteRole(String role) throws Exception;
|
||||
|
||||
Map<String, PersistedRole> getPersistedRoles();
|
||||
|
||||
/**
|
||||
* @return The ID with the stored counter
|
||||
*/
|
||||
|
|
|
@ -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.core.journal.EncodingSupport;
|
||||
|
||||
public class PersistedRoles implements EncodingSupport {
|
||||
public class PersistedSecuritySetting implements EncodingSupport {
|
||||
|
||||
// Constants -----------------------------------------------------
|
||||
|
||||
|
@ -54,7 +54,7 @@ public class PersistedRoles implements EncodingSupport {
|
|||
|
||||
// Constructors --------------------------------------------------
|
||||
|
||||
public PersistedRoles() {
|
||||
public PersistedSecuritySetting() {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,17 +70,17 @@ public class PersistedRoles implements EncodingSupport {
|
|||
* @param createAddressRoles
|
||||
* @param deleteAddressRoles
|
||||
*/
|
||||
public PersistedRoles(final String addressMatch,
|
||||
final String sendRoles,
|
||||
final String consumeRoles,
|
||||
final String createDurableQueueRoles,
|
||||
final String deleteDurableQueueRoles,
|
||||
final String createNonDurableQueueRoles,
|
||||
final String deleteNonDurableQueueRoles,
|
||||
final String manageRoles,
|
||||
final String browseRoles,
|
||||
final String createAddressRoles,
|
||||
final String deleteAddressRoles) {
|
||||
public PersistedSecuritySetting(final String addressMatch,
|
||||
final String sendRoles,
|
||||
final String consumeRoles,
|
||||
final String createDurableQueueRoles,
|
||||
final String deleteDurableQueueRoles,
|
||||
final String createNonDurableQueueRoles,
|
||||
final String deleteNonDurableQueueRoles,
|
||||
final String manageRoles,
|
||||
final String browseRoles,
|
||||
final String createAddressRoles,
|
||||
final String deleteAddressRoles) {
|
||||
super();
|
||||
this.addressMatch = SimpleString.toSimpleString(addressMatch);
|
||||
this.sendRoles = SimpleString.toSimpleString(sendRoles);
|
||||
|
@ -259,7 +259,7 @@ public class PersistedRoles implements EncodingSupport {
|
|||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
PersistedRoles other = (PersistedRoles) obj;
|
||||
PersistedSecuritySetting other = (PersistedSecuritySetting) obj;
|
||||
if (addressMatch == null) {
|
||||
if (other.addressMatch != null)
|
||||
return false;
|
||||
|
@ -325,7 +325,7 @@ public class PersistedRoles implements EncodingSupport {
|
|||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PersistedRoles [storeId=" + storeId +
|
||||
return "PersistedSecuritySetting [storeId=" + storeId +
|
||||
", addressMatch=" +
|
||||
addressMatch +
|
||||
", sendRoles=" +
|
|
@ -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.config.PersistedAddressSetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.journal.codec.AddressStatusEncoding;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.journal.codec.CursorAckRecordEncoding;
|
||||
|
@ -195,12 +197,16 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
|||
protected final Configuration config;
|
||||
|
||||
// Persisted core configuration
|
||||
protected final Map<SimpleString, PersistedRoles> mapPersistedRoles = new ConcurrentHashMap<>();
|
||||
protected final Map<SimpleString, PersistedSecuritySetting> mapPersistedSecuritySettings = new ConcurrentHashMap<>();
|
||||
|
||||
protected final Map<SimpleString, PersistedAddressSetting> mapPersistedAddressSettings = new ConcurrentHashMap<>();
|
||||
|
||||
protected final Map<String, PersistedDivertConfiguration> mapPersistedDivertConfigurations = new ConcurrentHashMap<>();
|
||||
|
||||
protected final Map<String, PersistedUser> mapPersistedUsers = new ConcurrentHashMap<>();
|
||||
|
||||
protected final Map<String, PersistedRole> mapPersistedRoles = new ConcurrentHashMap<>();
|
||||
|
||||
protected final ConcurrentLongHashMap<LargeServerMessage> largeMessagesToDelete = new ConcurrentLongHashMap<>();
|
||||
|
||||
public AbstractJournalStorageManager(final Configuration config,
|
||||
|
@ -767,20 +773,20 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<PersistedRoles> recoverPersistedRoles() throws Exception {
|
||||
return new ArrayList<>(mapPersistedRoles.values());
|
||||
public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
|
||||
return new ArrayList<>(mapPersistedSecuritySettings.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
|
||||
public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
|
||||
|
||||
deleteSecurityRoles(persistedRoles.getAddressMatch());
|
||||
deleteSecuritySetting(persistedRoles.getAddressMatch());
|
||||
readLock();
|
||||
try {
|
||||
final long id = idGenerator.generateID();
|
||||
persistedRoles.setStoreId(id);
|
||||
bindingsJournal.appendAddRecord(id, JournalRecordIds.SECURITY_RECORD, persistedRoles, true);
|
||||
mapPersistedRoles.put(persistedRoles.getAddressMatch(), persistedRoles);
|
||||
bindingsJournal.appendAddRecord(id, JournalRecordIds.SECURITY_SETTING_RECORD, persistedRoles, true);
|
||||
mapPersistedSecuritySettings.put(persistedRoles.getAddressMatch(), persistedRoles);
|
||||
} finally {
|
||||
readUnLock();
|
||||
}
|
||||
|
@ -818,6 +824,70 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
|||
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
|
||||
public void storeID(final long journalID, final long id) throws Exception {
|
||||
readLock();
|
||||
|
@ -852,8 +922,8 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
|||
}
|
||||
|
||||
@Override
|
||||
public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
|
||||
PersistedRoles oldRoles = mapPersistedRoles.remove(addressMatch);
|
||||
public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
|
||||
PersistedSecuritySetting oldRoles = mapPersistedSecuritySettings.remove(addressMatch);
|
||||
if (oldRoles != null) {
|
||||
readLock();
|
||||
try {
|
||||
|
@ -1560,9 +1630,9 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
|||
} else if (rec == JournalRecordIds.ADDRESS_SETTING_RECORD) {
|
||||
PersistedAddressSetting setting = newAddressEncoding(id, buffer);
|
||||
mapPersistedAddressSettings.put(setting.getAddressMatch(), setting);
|
||||
} else if (rec == JournalRecordIds.SECURITY_RECORD) {
|
||||
PersistedRoles roles = newSecurityRecord(id, buffer);
|
||||
mapPersistedRoles.put(roles.getAddressMatch(), roles);
|
||||
} else if (rec == JournalRecordIds.SECURITY_SETTING_RECORD) {
|
||||
PersistedSecuritySetting roles = newSecurityRecord(id, buffer);
|
||||
mapPersistedSecuritySettings.put(roles.getAddressMatch(), roles);
|
||||
} else if (rec == JournalRecordIds.QUEUE_STATUS_RECORD) {
|
||||
QueueStatusEncoding statusEncoding = newQueueStatusEncoding(id, buffer);
|
||||
PersistentQueueBindingEncoding queueBindingEncoding = mapBindings.get(statusEncoding.queueID);
|
||||
|
@ -1586,6 +1656,12 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
|||
} else if (rec == JournalRecordIds.DIVERT_RECORD) {
|
||||
PersistedDivertConfiguration divertConfiguration = newDivertEncoding(id, buffer);
|
||||
mapPersistedDivertConfigurations.put(divertConfiguration.getName(), divertConfiguration);
|
||||
} else if (rec == JournalRecordIds.USER_RECORD) {
|
||||
PersistedUser user = newUserEncoding(id, buffer);
|
||||
mapPersistedUsers.put(user.getUsername(), user);
|
||||
} else if (rec == JournalRecordIds.ROLE_RECORD) {
|
||||
PersistedRole role = newRoleEncoding(id, buffer);
|
||||
mapPersistedRoles.put(role.getUsername(), role);
|
||||
} else {
|
||||
// unlikely to happen
|
||||
ActiveMQServerLogger.LOGGER.invalidRecordType(rec, new Exception("invalid record type " + rec));
|
||||
|
@ -2042,8 +2118,8 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
|||
* @param buffer
|
||||
* @return
|
||||
*/
|
||||
protected static PersistedRoles newSecurityRecord(long id, ActiveMQBuffer buffer) {
|
||||
PersistedRoles roles = new PersistedRoles();
|
||||
protected static PersistedSecuritySetting newSecurityRecord(long id, ActiveMQBuffer buffer) {
|
||||
PersistedSecuritySetting roles = new PersistedSecuritySetting();
|
||||
roles.decode(buffer);
|
||||
roles.setStoreId(id);
|
||||
return roles;
|
||||
|
@ -2074,6 +2150,20 @@ public abstract class AbstractJournalStorageManager extends CriticalComponentImp
|
|||
persistedDivertConfiguration.setStoreId(id);
|
||||
return persistedDivertConfiguration;
|
||||
}
|
||||
|
||||
static PersistedUser newUserEncoding(long id, ActiveMQBuffer buffer) {
|
||||
PersistedUser persistedUser = new PersistedUser();
|
||||
persistedUser.decode(buffer);
|
||||
persistedUser.setStoreId(id);
|
||||
return persistedUser;
|
||||
}
|
||||
|
||||
static PersistedRole newRoleEncoding(long id, ActiveMQBuffer buffer) {
|
||||
PersistedRole persistedRole = new PersistedRole();
|
||||
persistedRole.decode(buffer);
|
||||
persistedRole.setStoreId(id);
|
||||
return persistedRole;
|
||||
}
|
||||
/**
|
||||
* @param id
|
||||
* @param buffer
|
||||
|
|
|
@ -88,9 +88,11 @@ import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalR
|
|||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.PAGE_TRANSACTION;
|
||||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.QUEUE_BINDING_RECORD;
|
||||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.QUEUE_STATUS_RECORD;
|
||||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.SECURITY_RECORD;
|
||||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.ROLE_RECORD;
|
||||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.SECURITY_SETTING_RECORD;
|
||||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.SET_SCHEDULED_DELIVERY_TIME;
|
||||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.UPDATE_DELIVERY_COUNT;
|
||||
import static org.apache.activemq.artemis.core.persistence.impl.journal.JournalRecordIds.USER_RECORD;
|
||||
|
||||
/**
|
||||
* Outputs a String description of the Journals contents.
|
||||
|
@ -685,7 +687,7 @@ public final class DescribeJournal {
|
|||
case ADDRESS_SETTING_RECORD:
|
||||
return AbstractJournalStorageManager.newAddressEncoding(id, buffer);
|
||||
|
||||
case SECURITY_RECORD:
|
||||
case SECURITY_SETTING_RECORD:
|
||||
return AbstractJournalStorageManager.newSecurityRecord(id, buffer);
|
||||
|
||||
case ADDRESS_BINDING_RECORD:
|
||||
|
@ -694,6 +696,12 @@ public final class DescribeJournal {
|
|||
case ADDRESS_STATUS_RECORD:
|
||||
return AbstractJournalStorageManager.newAddressStatusEncoding(id, buffer);
|
||||
|
||||
case USER_RECORD:
|
||||
return AbstractJournalStorageManager.newUserEncoding(id, buffer);
|
||||
|
||||
case ROLE_RECORD:
|
||||
return AbstractJournalStorageManager.newRoleEncoding(id, buffer);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ public final class JournalRecordIds {
|
|||
|
||||
public static final byte ADDRESS_SETTING_RECORD = 25;
|
||||
|
||||
public static final byte SECURITY_RECORD = 26;
|
||||
public static final byte SECURITY_SETTING_RECORD = 26;
|
||||
|
||||
public static final byte DIVERT_RECORD = 27;
|
||||
|
||||
|
@ -92,4 +92,8 @@ public final class JournalRecordIds {
|
|||
|
||||
public static final byte ADDRESS_STATUS_RECORD = 46;
|
||||
|
||||
public static final byte USER_RECORD = 47;
|
||||
|
||||
public static final byte ROLE_RECORD = 48;
|
||||
|
||||
}
|
||||
|
|
|
@ -49,7 +49,9 @@ import org.apache.activemq.artemis.core.persistence.AddressQueueStatus;
|
|||
import org.apache.activemq.artemis.core.persistence.StorageManager;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
|
||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
||||
|
@ -438,7 +440,7 @@ public class NullStorageManager implements StorageManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<PersistedRoles> recoverPersistedRoles() throws Exception {
|
||||
public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
@ -456,7 +458,33 @@ public class NullStorageManager implements StorageManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void storeSecurityRoles(final PersistedRoles persistedRoles) throws Exception {
|
||||
public void storeUser(PersistedUser persistedUser) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteUser(String username) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, PersistedUser> getPersistedUsers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeRole(PersistedRole persistedRole) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteRole(String role) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, PersistedRole> getPersistedRoles() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeSecuritySetting(final PersistedSecuritySetting persistedRoles) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -464,7 +492,7 @@ public class NullStorageManager implements StorageManager {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void deleteSecurityRoles(final SimpleString addressMatch) throws Exception {
|
||||
public void deleteSecuritySetting(final SimpleString addressMatch) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
*/
|
||||
package org.apache.activemq.artemis.core.security;
|
||||
|
||||
import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
|
||||
|
||||
public class User {
|
||||
|
||||
final String user;
|
||||
|
||||
final String password;
|
||||
String password;
|
||||
|
||||
public User(final String user, final String password) {
|
||||
this.user = user;
|
||||
|
@ -54,7 +56,8 @@ public class User {
|
|||
if (user == null) {
|
||||
return false;
|
||||
}
|
||||
return this.user.equals(user) && this.password.equals(password);
|
||||
|
||||
return this.user.equals(user) && PasswordMaskingUtil.getHashProcessor(this.password).compare(password != null ? password.toCharArray() : null, this.password);
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
|
@ -64,4 +67,8 @@ public class User {
|
|||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -495,4 +495,7 @@ public interface ActiveMQMessageBundle {
|
|||
|
||||
@Message(id = 229232, value = "Cannot create consumer on {0}. Session is closed.", format = Message.Format.MESSAGE_FORMAT)
|
||||
ActiveMQIllegalStateException cannotCreateConsumerOnClosedSession(SimpleString queueName);
|
||||
|
||||
@Message(id = 229233, value = "Cannot set ActiveMQSecurityManager during startup or while started")
|
||||
IllegalStateException cannotSetSecurityManager();
|
||||
}
|
||||
|
|
|
@ -833,6 +833,10 @@ public interface ActiveMQServer extends ServiceComponent {
|
|||
|
||||
void setMBeanServer(MBeanServer mBeanServer);
|
||||
|
||||
MBeanServer getMBeanServer();
|
||||
|
||||
void setSecurityManager(ActiveMQSecurityManager securityManager);
|
||||
|
||||
/**
|
||||
* Adding external components is allowed only if the state
|
||||
* isn't {@link SERVER_STATE#STOPPED} or {@link SERVER_STATE#STOPPING}.<br>
|
||||
|
|
|
@ -1717,6 +1717,14 @@ public interface ActiveMQServerLogger extends BasicLogger {
|
|||
format = Message.Format.MESSAGE_FORMAT)
|
||||
void unableStartManagementContext(@Cause Exception e);
|
||||
|
||||
@LogMessage(level = Logger.Level.WARN)
|
||||
@Message(id = 222298, value = "Failed to create bootstrap user \"{0}\". User management may not function.", format = Message.Format.MESSAGE_FORMAT)
|
||||
void failedToCreateBootstrapCredentials(@Cause Exception e, String user);
|
||||
|
||||
@LogMessage(level = Logger.Level.WARN)
|
||||
@Message(id = 222299, value = "No bootstrap credentials found. User management may not function.", format = Message.Format.MESSAGE_FORMAT)
|
||||
void noBootstrapCredentialsFound();
|
||||
|
||||
@LogMessage(level = Logger.Level.ERROR)
|
||||
@Message(id = 224000, value = "Failure in initialisation", format = Message.Format.MESSAGE_FORMAT)
|
||||
void initializationError(@Cause Throwable e);
|
||||
|
|
|
@ -94,7 +94,7 @@ import org.apache.activemq.artemis.core.persistence.QueueBindingInfo;
|
|||
import org.apache.activemq.artemis.core.persistence.StorageManager;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.journal.JDBCJournalStorageManager;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.journal.JournalStorageManager;
|
||||
|
@ -186,6 +186,7 @@ import org.apache.activemq.artemis.logs.AuditLogger;
|
|||
import org.apache.activemq.artemis.spi.core.protocol.ProtocolManagerFactory;
|
||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
|
||||
import org.apache.activemq.artemis.spi.core.protocol.SessionCallback;
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
|
||||
import org.apache.activemq.artemis.utils.ActiveMQThreadFactory;
|
||||
import org.apache.activemq.artemis.utils.ActiveMQThreadPoolExecutor;
|
||||
|
@ -236,7 +237,7 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
|||
|
||||
private final Version version;
|
||||
|
||||
private final ActiveMQSecurityManager securityManager;
|
||||
private ActiveMQSecurityManager securityManager;
|
||||
|
||||
private final Configuration configuration;
|
||||
|
||||
|
@ -869,6 +870,19 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
|||
this.mbeanServer = mbeanServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MBeanServer getMBeanServer() {
|
||||
return mbeanServer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSecurityManager(ActiveMQSecurityManager securityManager) {
|
||||
if (state == SERVER_STATE.STARTING || state == SERVER_STATE.STARTED) {
|
||||
throw ActiveMQMessageBundle.BUNDLE.cannotSetSecurityManager();
|
||||
}
|
||||
this.securityManager = securityManager;
|
||||
}
|
||||
|
||||
private void validateAddExternalComponent(ActiveMQComponent externalComponent) {
|
||||
final SERVER_STATE state = this.state;
|
||||
if (state == SERVER_STATE.STOPPED || state == SERVER_STATE.STOPPING) {
|
||||
|
@ -3048,6 +3062,10 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
|||
|
||||
removeExtraAddressStores();
|
||||
|
||||
if (securityManager instanceof ActiveMQBasicSecurityManager) {
|
||||
((ActiveMQBasicSecurityManager)securityManager).completeInit(storageManager);
|
||||
}
|
||||
|
||||
final ServerInfo dumper = new ServerInfo(this, pagingManager);
|
||||
|
||||
long dumpInfoInterval = configuration.getServerDumpInterval();
|
||||
|
@ -3381,6 +3399,8 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO load users/roles
|
||||
|
||||
journalLoader.cleanUp();
|
||||
|
||||
return journalInfo;
|
||||
|
@ -3395,9 +3415,9 @@ public class ActiveMQServerImpl implements ActiveMQServer {
|
|||
addressSettingsRepository.addMatch(set.getAddressMatch().toString(), set.getSetting());
|
||||
}
|
||||
|
||||
List<PersistedRoles> roles = storageManager.recoverPersistedRoles();
|
||||
List<PersistedSecuritySetting> roles = storageManager.recoverSecuritySettings();
|
||||
|
||||
for (PersistedRoles roleItem : roles) {
|
||||
for (PersistedSecuritySetting roleItem : roles) {
|
||||
Set<Role> setRoles = SecurityFormatter.createSecurity(roleItem.getSendRoles(), roleItem.getConsumeRoles(), roleItem.getCreateDurableQueueRoles(), roleItem.getDeleteDurableQueueRoles(), roleItem.getCreateNonDurableQueueRoles(), roleItem.getDeleteNonDurableQueueRoles(), roleItem.getManageRoles(), roleItem.getBrowseRoles(), roleItem.getCreateAddressRoles(), roleItem.getDeleteAddressRoles());
|
||||
|
||||
securityRepository.addMatch(roleItem.getAddressMatch().toString(), setRoles);
|
||||
|
|
|
@ -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.server.ActiveMQComponent;
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager;
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXAuthenticator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -32,9 +35,11 @@ public class ManagementConnector implements ActiveMQComponent {
|
|||
private ConnectorServerFactory connectorServerFactory;
|
||||
private RmiRegistryFactory rmiRegistryFactory;
|
||||
private MBeanServerFactory mbeanServerFactory;
|
||||
private ActiveMQSecurityManager securityManager;
|
||||
|
||||
public ManagementConnector(JMXConnectorConfiguration configuration) {
|
||||
public ManagementConnector(JMXConnectorConfiguration configuration, ActiveMQSecurityManager securityManager) {
|
||||
this.configuration = configuration;
|
||||
this.securityManager = securityManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -54,8 +59,15 @@ public class ManagementConnector implements ActiveMQComponent {
|
|||
|
||||
MBeanServer mbeanServer = mbeanServerFactory.getServer();
|
||||
|
||||
JaasAuthenticator jaasAuthenticator = new JaasAuthenticator();
|
||||
jaasAuthenticator.setRealm(configuration.getJmxRealm());
|
||||
JMXAuthenticator authenticator;
|
||||
|
||||
if (securityManager != null && securityManager instanceof ActiveMQBasicSecurityManager) {
|
||||
authenticator = new BasicAuthenticator((ActiveMQBasicSecurityManager) securityManager);
|
||||
} else {
|
||||
JaasAuthenticator jaasAuthenticator = new JaasAuthenticator();
|
||||
jaasAuthenticator.setRealm(configuration.getJmxRealm());
|
||||
authenticator = jaasAuthenticator;
|
||||
}
|
||||
|
||||
connectorServerFactory = new ConnectorServerFactory();
|
||||
connectorServerFactory.setServer(mbeanServer);
|
||||
|
@ -63,7 +75,7 @@ public class ManagementConnector implements ActiveMQComponent {
|
|||
connectorServerFactory.setRmiServerHost(configuration.getConnectorHost());
|
||||
connectorServerFactory.setObjectName(new ObjectName(configuration.getObjectName()));
|
||||
Map<String, Object> environment = new HashMap<>();
|
||||
environment.put("jmx.remote.authenticator", jaasAuthenticator);
|
||||
environment.put("jmx.remote.authenticator", authenticator);
|
||||
try {
|
||||
connectorServerFactory.setEnvironment(environment);
|
||||
connectorServerFactory.setAuthenticatorType(configuration.getAuthenticatorType());
|
||||
|
@ -108,4 +120,7 @@ public class ManagementConnector implements ActiveMQComponent {
|
|||
}
|
||||
}
|
||||
|
||||
public ConnectorServerFactory getConnectorServerFactory() {
|
||||
return connectorServerFactory;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.apache.activemq.artemis.core.server.ServiceComponent;
|
|||
import org.apache.activemq.artemis.core.server.management.impl.HawtioSecurityControlImpl;
|
||||
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
|
||||
|
||||
public class ManagementContext implements ServiceComponent {
|
||||
private AtomicBoolean isStarted = new AtomicBoolean(false);
|
||||
|
@ -32,6 +33,7 @@ public class ManagementContext implements ServiceComponent {
|
|||
private JMXConnectorConfiguration jmxConnectorConfiguration;
|
||||
private ManagementConnector mBeanServer;
|
||||
private ArtemisMBeanServerGuard guardHandler;
|
||||
private ActiveMQSecurityManager securityManager;
|
||||
|
||||
@Override
|
||||
public void start() throws Exception {
|
||||
|
@ -44,7 +46,7 @@ public class ManagementContext implements ServiceComponent {
|
|||
}
|
||||
|
||||
if (jmxConnectorConfiguration != null) {
|
||||
mBeanServer = new ManagementConnector(jmxConnectorConfiguration);
|
||||
mBeanServer = new ManagementConnector(jmxConnectorConfiguration, securityManager);
|
||||
mBeanServer.start();
|
||||
}
|
||||
isStarted.set(true);
|
||||
|
@ -99,4 +101,16 @@ public class ManagementContext implements ServiceComponent {
|
|||
public ArtemisMBeanServerGuard getArtemisMBeanServerGuard() {
|
||||
return guardHandler;
|
||||
}
|
||||
|
||||
public void setSecurityManager(ActiveMQSecurityManager securityManager) {
|
||||
this.securityManager = securityManager;
|
||||
}
|
||||
|
||||
public ActiveMQSecurityManager getSecurityManager() {
|
||||
return securityManager;
|
||||
}
|
||||
|
||||
public ManagementConnector getManagementConnector() {
|
||||
return mBeanServer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.login.LoginContext;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.Principal;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
|
||||
import org.apache.activemq.artemis.core.security.CheckType;
|
||||
import org.apache.activemq.artemis.core.security.Role;
|
||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||
import org.apache.activemq.artemis.logs.AuditLogger;
|
||||
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.JaasCallbackHandler;
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
|
||||
import org.apache.activemq.artemis.utils.SecurityManagerUtil;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.apache.activemq.artemis.core.remoting.CertificateUtil.getCertsFromConnection;
|
||||
|
@ -48,8 +43,6 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(ActiveMQJAASSecurityManager.class);
|
||||
|
||||
private static final String WILDCARD = "*";
|
||||
|
||||
private String configurationName;
|
||||
private String certificateConfigurationName;
|
||||
private SecurityConfiguration configuration;
|
||||
|
@ -115,33 +108,10 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
|
|||
final Set<Role> roles,
|
||||
final CheckType checkType,
|
||||
final String address) {
|
||||
boolean authorized = false;
|
||||
boolean authorized = SecurityManagerUtil.authorize(subject, roles, checkType, rolePrincipalClass);
|
||||
|
||||
if (subject != null) {
|
||||
Set<RolePrincipal> rolesWithPermission = getPrincipalsInRole(checkType, roles);
|
||||
|
||||
// Check the caller's roles
|
||||
Set<Principal> rolesForSubject = new HashSet<>();
|
||||
try {
|
||||
rolesForSubject.addAll(subject.getPrincipals(Class.forName(rolePrincipalClass).asSubclass(Principal.class)));
|
||||
} catch (Exception e) {
|
||||
ActiveMQServerLogger.LOGGER.failedToFindRolesForTheSubject(e);
|
||||
}
|
||||
if (rolesForSubject.size() > 0 && rolesWithPermission.size() > 0) {
|
||||
Iterator<Principal> rolesForSubjectIter = rolesForSubject.iterator();
|
||||
while (!authorized && rolesForSubjectIter.hasNext()) {
|
||||
Iterator<RolePrincipal> rolesWithPermissionIter = rolesWithPermission.iterator();
|
||||
Principal subjectRole = rolesForSubjectIter.next();
|
||||
while (!authorized && rolesWithPermissionIter.hasNext()) {
|
||||
Principal roleWithPermission = rolesWithPermissionIter.next();
|
||||
authorized = subjectRole.equals(roleWithPermission);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("user " + (authorized ? " is " : " is NOT ") + "authorized");
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("user " + (authorized ? " is " : " is NOT ") + "authorized");
|
||||
}
|
||||
|
||||
return authorized;
|
||||
|
@ -187,20 +157,6 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
|
|||
}
|
||||
}
|
||||
|
||||
private Set<RolePrincipal> getPrincipalsInRole(final CheckType checkType, final Set<Role> roles) {
|
||||
Set principals = new HashSet<>();
|
||||
for (Role role : roles) {
|
||||
if (checkType.hasRole(role)) {
|
||||
try {
|
||||
principals.add(createGroupPrincipal(role.getName(), rolePrincipalClass));
|
||||
} catch (Exception e) {
|
||||
ActiveMQServerLogger.LOGGER.failedAddRolePrincipal(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return principals;
|
||||
}
|
||||
|
||||
public void setConfigurationName(final String configurationName) {
|
||||
this.configurationName = configurationName;
|
||||
}
|
||||
|
@ -240,60 +196,4 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
|
|||
public void setRolePrincipalClass(String rolePrincipalClass) {
|
||||
this.rolePrincipalClass = rolePrincipalClass;
|
||||
}
|
||||
|
||||
public static Object createGroupPrincipal(String name, String groupClass) throws Exception {
|
||||
if (WILDCARD.equals(name)) {
|
||||
// simple match all group principal - match any name and class
|
||||
return new Principal() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return WILDCARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return WILDCARD.hashCode();
|
||||
}
|
||||
};
|
||||
}
|
||||
Object[] param = new Object[]{name};
|
||||
|
||||
Class<?> cls = Class.forName(groupClass);
|
||||
|
||||
Constructor<?>[] constructors = cls.getConstructors();
|
||||
int i;
|
||||
Object instance;
|
||||
for (i = 0; i < constructors.length; i++) {
|
||||
Class<?>[] paramTypes = constructors[i].getParameterTypes();
|
||||
if (paramTypes.length != 0 && paramTypes[0].equals(String.class)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < constructors.length) {
|
||||
instance = constructors[i].newInstance(param);
|
||||
} else {
|
||||
instance = cls.newInstance();
|
||||
Method[] methods = cls.getMethods();
|
||||
i = 0;
|
||||
for (i = 0; i < methods.length; i++) {
|
||||
Class<?>[] paramTypes = methods[i].getParameterTypes();
|
||||
if (paramTypes.length != 0 && methods[i].getName().equals("setName") && paramTypes[0].equals(String.class)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < methods.length) {
|
||||
methods[i].invoke(instance, param);
|
||||
} else {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
try {
|
||||
return PasswordMaskingUtil.resolveMask(null, password, codecClass);
|
||||
return PasswordMaskingUtil.resolveMask(password, codecClass);
|
||||
} catch (Exception 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.core.server.ActiveMQMessageBundle;
|
||||
import org.apache.activemq.artemis.spi.core.security.UserManagement;
|
||||
import org.apache.activemq.artemis.utils.StringUtil;
|
||||
import org.apache.commons.configuration2.PropertiesConfiguration;
|
||||
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
|
||||
|
@ -37,7 +38,7 @@ import org.apache.commons.configuration2.builder.fluent.Configurations;
|
|||
import static org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule.ROLE_FILE_PROP_NAME;
|
||||
import static org.apache.activemq.artemis.spi.core.security.jaas.PropertiesLoginModule.USER_FILE_PROP_NAME;
|
||||
|
||||
public class PropertiesLoginModuleConfigurator {
|
||||
public class PropertiesLoginModuleConfigurator implements UserManagement {
|
||||
|
||||
private static final String LICENSE_HEADER =
|
||||
"## ---------------------------------------------------------------------------\n" +
|
||||
|
@ -125,7 +126,8 @@ public class PropertiesLoginModuleConfigurator {
|
|||
}
|
||||
}
|
||||
|
||||
public void addNewUser(String username, String hash, String... roles) throws Exception {
|
||||
@Override
|
||||
public void addNewUser(String username, String hash, String... roles) {
|
||||
if (userConfig.getString(username) != null) {
|
||||
throw ActiveMQMessageBundle.BUNDLE.userAlreadyExists(username);
|
||||
}
|
||||
|
@ -143,6 +145,7 @@ public class PropertiesLoginModuleConfigurator {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUser(String username) {
|
||||
if (userConfig.getProperty(username) == null) {
|
||||
throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
|
||||
|
@ -151,6 +154,7 @@ public class PropertiesLoginModuleConfigurator {
|
|||
removeRoles(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Set<String>> listUser(String username) {
|
||||
Map<String, Set<String>> result = new HashMap<>();
|
||||
|
||||
|
@ -166,7 +170,8 @@ public class PropertiesLoginModuleConfigurator {
|
|||
return result;
|
||||
}
|
||||
|
||||
public void updateUser(String username, String password, String[] roles) {
|
||||
@Override
|
||||
public void updateUser(String username, String password, String... roles) {
|
||||
String oldPassword = (String) userConfig.getProperty(username);
|
||||
if (oldPassword == null) {
|
||||
throw ActiveMQMessageBundle.BUNDLE.userDoesNotExist(username);
|
||||
|
|
|
@ -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.config.PersistedAddressSetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
|
||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
||||
|
@ -597,17 +599,17 @@ public class TransactionImplTest extends ActiveMQTestBase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
|
||||
public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
|
||||
public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PersistedRoles> recoverPersistedRoles() throws Exception {
|
||||
public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -626,6 +628,36 @@ public class TransactionImplTest extends ActiveMQTestBase {
|
|||
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
|
||||
public long storePageCounter(long txID, long queueID, long value, long size) throws Exception {
|
||||
return 0;
|
||||
|
|
|
@ -352,16 +352,23 @@ the Transport](configuring-transports.md).
|
|||
|
||||
## User credentials
|
||||
|
||||
Apache ActiveMQ Artemis ships with two security manager implementations:
|
||||
|
||||
- The legacy, deprecated `ActiveMQSecurityManager` that reads user credentials,
|
||||
i.e. user names, passwords and role information from properties files on the
|
||||
classpath called `artemis-users.properties` and `artemis-roles.properties`.
|
||||
Apache ActiveMQ Artemis ships with three security manager implementations:
|
||||
|
||||
- The flexible, pluggable `ActiveMQJAASSecurityManager` which supports any
|
||||
standard JAAS login module. Artemis ships with several login modules which
|
||||
will be discussed further down. This is the default security manager.
|
||||
|
||||
- The `ActiveMQBasicSecurityManager` which doesn't use JAAS and only supports
|
||||
auth via username & password credentials. It also supports adding, removing,
|
||||
and updating users via the management API. All user & role data is stored
|
||||
in the broker's bindings journal which means any changes made to a live
|
||||
broker will be available on its backup.
|
||||
|
||||
- The legacy, deprecated `ActiveMQSecurityManagerImpl` that reads user
|
||||
credentials, i.e. user names, passwords and role information from properties
|
||||
files on the classpath called `artemis-users.properties` and
|
||||
`artemis-roles.properties`.
|
||||
|
||||
### JAAS Security Manager
|
||||
|
||||
When using the Java Authentication and Authorization Service (JAAS) much of the
|
||||
|
@ -1099,6 +1106,90 @@ superseded by SASL GSSAPI. However, for clients that don't support SASL (core
|
|||
client), using TLS can provide Kerberos authentication over an *unsecure*
|
||||
channel.
|
||||
|
||||
### Basic Security Manager
|
||||
|
||||
As the name suggests, the `ActiveMQBasicSecurityManager` is _basic_. It is not
|
||||
pluggable like the JAAS security manager and it _only_ supports authentication
|
||||
via username and password credentials. Furthermore, the the Hawtio-based web
|
||||
console requires JAAS. Therefore you will *still need* to configure a
|
||||
`login.config` if you plan on using the web console. However, this security
|
||||
manager *may* still may have a couple of advantages depending on your use-case.
|
||||
|
||||
All user & role data is stored in the bindings journal (or bindings table if
|
||||
using JDBC). The advantage here is that in a live/backup use-case any user
|
||||
management performed on the live broker will be reflected on the backup upon
|
||||
failover. Typically LDAP would be employed for this kind of use-case, but not
|
||||
everyone wants or is able to administer an independent LDAP server.
|
||||
|
||||
User management is provided by the broker's management API. This includes the
|
||||
ability to add, list, update, and remove users & roles. As with all management
|
||||
functions, this is available via JMX, management messages, HTTP (via Jolokia),
|
||||
web console, etc. These functions are also available from the ActiveMQ Artemis
|
||||
command-line interface. Having the broker store this data directly means that
|
||||
it must be running in order to manage users. There is no way to modify the
|
||||
bindings data manually.
|
||||
|
||||
To be clear, any management access via HTTP (e.g. web console or Jolokia) will
|
||||
go through Hawtio JAAS. MBean access via JConsole or other remote JMX tool will
|
||||
go through the basic security manager. Management messages will also go through
|
||||
the basic security manager.
|
||||
|
||||
#### Configuration
|
||||
|
||||
The configuration for the `ActiveMQBasicSecurityManager` happens in
|
||||
`bootstrap.xml` just like it does for all security manager implementations.
|
||||
Here's an example:
|
||||
|
||||
```xml
|
||||
<broker xmlns="http://activemq.org/schema">
|
||||
|
||||
<security-manager class-name="org.apache.activemq.artemis.spi.core.security.ActiveMQBasicSecurityManager">
|
||||
<property key="bootstrapUser" value="myUser"/>
|
||||
<property key="bootstrapPassword" value="myPass"/>
|
||||
<property key="bootstrapRole" value="myRole"/>
|
||||
</security-manager>
|
||||
|
||||
...
|
||||
</broker>
|
||||
```
|
||||
|
||||
Because the bindings data which holds the user & role data cannot be modified
|
||||
manually and because the broker must be running to manage users and because
|
||||
the broker often needs to be secured from first boot the
|
||||
`ActiveMQBasicSecurityManager` has 3 properties to define a user whose
|
||||
credentials can then be used to add other users.
|
||||
|
||||
- `bootstrapUser` - the name of the bootstrap user
|
||||
- `bootstrapPassword` - the password for the bootstrap user; supports masking
|
||||
- `bootstrapRole` - the role of the bootstrap user
|
||||
|
||||
The value specified in the `bootstrapRole` will need the following permissions
|
||||
on the `activemq.management` address:
|
||||
|
||||
- `createNonDurableQueue`
|
||||
- `createAddress`
|
||||
- `consume`
|
||||
- `manage`
|
||||
- `send`
|
||||
|
||||
For example:
|
||||
|
||||
```xml
|
||||
<security-setting match="activemq.management.#">
|
||||
<permission type="createNonDurableQueue" roles="myRole"/>
|
||||
<permission type="createAddress" roles="myRole"/>
|
||||
<permission type="consume" roles="myRole"/>
|
||||
<permission type="manage" roles="myRole"/>
|
||||
<permission type="send" roles="myRole"/>
|
||||
</security-setting>
|
||||
```
|
||||
|
||||
> **Note:**
|
||||
>
|
||||
> If the 3 `bootstrap` properties are defined then those credentials will be
|
||||
> set whenever you start the broker no matter what changes may have been made
|
||||
> to them at runtime previously.
|
||||
|
||||
## Mapping external roles
|
||||
Roles from external authentication providers (i.e. LDAP) can be mapped to internally used roles. The is done through role-mapping entries in the security-settings block:
|
||||
|
||||
|
|
|
@ -62,7 +62,9 @@ import org.apache.activemq.artemis.core.persistence.AddressQueueStatus;
|
|||
import org.apache.activemq.artemis.core.persistence.StorageManager;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedAddressSetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedDivertConfiguration;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRole;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedUser;
|
||||
import org.apache.activemq.artemis.core.persistence.impl.PageCountPending;
|
||||
import org.apache.activemq.artemis.core.postoffice.Binding;
|
||||
import org.apache.activemq.artemis.core.postoffice.PostOffice;
|
||||
|
@ -694,18 +696,18 @@ public class SendAckFailTest extends SpawnedTestBase {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void storeSecurityRoles(PersistedRoles persistedRoles) throws Exception {
|
||||
manager.storeSecurityRoles(persistedRoles);
|
||||
public void storeSecuritySetting(PersistedSecuritySetting persistedRoles) throws Exception {
|
||||
manager.storeSecuritySetting(persistedRoles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSecurityRoles(SimpleString addressMatch) throws Exception {
|
||||
manager.deleteSecurityRoles(addressMatch);
|
||||
public void deleteSecuritySetting(SimpleString addressMatch) throws Exception {
|
||||
manager.deleteSecuritySetting(addressMatch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PersistedRoles> recoverPersistedRoles() throws Exception {
|
||||
return manager.recoverPersistedRoles();
|
||||
public List<PersistedSecuritySetting> recoverSecuritySettings() throws Exception {
|
||||
return manager.recoverSecuritySettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -723,6 +725,36 @@ public class SendAckFailTest extends SpawnedTestBase {
|
|||
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
|
||||
public long storePageCounter(long txID, long queueID, long value, long size) throws Exception {
|
||||
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.core.config.StoreConfiguration;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedRoles;
|
||||
import org.apache.activemq.artemis.core.persistence.config.PersistedSecuritySetting;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
||||
|
||||
private Map<SimpleString, PersistedRoles> mapExpectedSets;
|
||||
private Map<SimpleString, PersistedSecuritySetting> mapExpectedSets;
|
||||
|
||||
public RolesConfigurationStorageTest(StoreConfiguration.StoreType storeType) {
|
||||
super(storeType);
|
||||
|
@ -41,18 +41,18 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
|||
mapExpectedSets = new HashMap<>();
|
||||
}
|
||||
|
||||
protected void addSetting(PersistedRoles setting) throws Exception {
|
||||
protected void addSetting(PersistedSecuritySetting setting) throws Exception {
|
||||
mapExpectedSets.put(setting.getAddressMatch(), setting);
|
||||
journal.storeSecurityRoles(setting);
|
||||
journal.storeSecuritySetting(setting);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoreSecuritySettings() throws Exception {
|
||||
createStorage();
|
||||
|
||||
addSetting(new PersistedRoles("a#", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
addSetting(new PersistedSecuritySetting("a#", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
|
||||
addSetting(new PersistedRoles("a2", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
addSetting(new PersistedSecuritySetting("a2", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
|
||||
journal.stop();
|
||||
|
||||
|
@ -62,9 +62,9 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
|||
|
||||
checkSettings();
|
||||
|
||||
addSetting(new PersistedRoles("a2", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
addSetting(new PersistedSecuritySetting("a2", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
|
||||
addSetting(new PersistedRoles("a3", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
addSetting(new PersistedSecuritySetting("a3", "a1", null, "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
|
||||
checkSettings();
|
||||
|
||||
|
@ -92,7 +92,7 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
|||
|
||||
checkSettings();
|
||||
|
||||
addSetting(new PersistedRoles("a#", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
addSetting(new PersistedSecuritySetting("a#", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1", "a1"));
|
||||
|
||||
journal.stop();
|
||||
|
||||
|
@ -112,12 +112,12 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
|
|||
* @throws Exception
|
||||
*/
|
||||
private void checkSettings() throws Exception {
|
||||
List<PersistedRoles> listSetting = journal.recoverPersistedRoles();
|
||||
List<PersistedSecuritySetting> listSetting = journal.recoverSecuritySettings();
|
||||
|
||||
assertEquals(mapExpectedSets.size(), listSetting.size());
|
||||
|
||||
for (PersistedRoles el : listSetting) {
|
||||
PersistedRoles el2 = mapExpectedSets.get(el.getAddressMatch());
|
||||
for (PersistedSecuritySetting el : listSetting) {
|
||||
PersistedSecuritySetting el2 = mapExpectedSets.get(el.getAddressMatch());
|
||||
|
||||
assertEquals(el, el2);
|
||||
}
|
||||
|
|
|
@ -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