ARTEMIS-4582 - view and edit permissions, mops. security-settings for rbac on management apis

This commit is contained in:
Gary Tully 2024-02-14 14:08:08 +00:00
parent 576622571a
commit 2e17a4a007
116 changed files with 3845 additions and 458 deletions

View File

@ -321,7 +321,7 @@ public final class XmlDataImporter extends ActionAbstract {
}
for (String queue : queues) {
long queueID;
long queueID = -1;
if (queueIDs.containsKey(queue)) {
queueID = queueIDs.get(queue);
@ -337,17 +337,27 @@ public final class XmlDataImporter extends ActionAbstract {
logger.debug("Requesting ID for: {}", queue);
}
ClientMessage reply = requestor.request(managementMessage);
Number idObject = (Number) ManagementHelper.getResult(reply);
queueID = idObject.longValue();
if (ManagementHelper.hasOperationSucceeded(reply)) {
Number idObject = (Number) ManagementHelper.getResult(reply);
queueID = idObject.longValue();
} else {
if (debugLog) {
logger.debug("Failed to get ID for {}, reply: {}", queue, ManagementHelper.getResult(reply, String.class));
}
}
}
if (debugLog) {
logger.debug("ID for {} is: {}", queue, queueID);
}
queueIDs.put(queue, queueID); // store it so we don't have to look it up every time
if (queueID != -1) {
queueIDs.put(queue, queueID); // store it so we don't have to look it up every time
}
}
buffer.putLong(queueID);
if (queueID != -1) {
buffer.putLong(queueID);
}
if (debugLog) {
debugLogMessage.append(queue).append(", ");
}

View File

@ -692,6 +692,12 @@ public final class ActiveMQDefaultConfiguration {
public static final String DEFAULT_LITERAL_MATCH_MARKERS = null;
private static final String DEFAULT_VIEW_PERMISSION_METHOD_MATCH_PATTERN = "^(get|is|count|list|browse|query).*$";
private static final String DEFAULT_MANAGEMENT_RBAC_PREFIX = "mops";
private static final boolean DEFAULT_MANAGEMENT_MESSAGE_RBAC = false;
/**
* If true then the ActiveMQ Artemis Server will make use of any Protocol Managers that are in available on the classpath. If false then only the core protocol will be available, unless in Embedded mode where users can inject their own Protocol Managers.
*/
@ -1900,4 +1906,16 @@ public final class ActiveMQDefaultConfiguration {
public static String getLiteralMatchMarkers() {
return DEFAULT_LITERAL_MATCH_MARKERS;
}
public static String getViewPermissionMethodMatchPattern() {
return DEFAULT_VIEW_PERMISSION_METHOD_MATCH_PATTERN;
}
public static String getManagementRbacPrefix() {
return DEFAULT_MANAGEMENT_RBAC_PREFIX;
}
public static boolean getManagementMessagesRbac() {
return DEFAULT_MANAGEMENT_MESSAGE_RBAC;
}
}

View File

@ -1406,6 +1406,21 @@ public interface ActiveMQServerControl {
@Parameter(desc = "a comma-separated list of roles allowed to create addresses", name = "createAddressRoles") String createAddressRoles,
@Parameter(desc = "a comma-separated list of roles allowed to delete addresses", name = "deleteAddressRoles") String deleteAddressRoles) throws Exception;
@Operation(desc = "Add security settings for addresses matching the addressMatch", impact = MBeanOperationInfo.ACTION)
void addSecuritySettings(@Parameter(desc = "an address match", name = "addressMatch") String addressMatch,
@Parameter(desc = "a comma-separated list of roles allowed to send messages", name = "send") String sendRoles,
@Parameter(desc = "a comma-separated list of roles allowed to consume messages", name = "consume") String consumeRoles,
@Parameter(desc = "a comma-separated list of roles allowed to create durable queues", name = "createDurableQueueRoles") String createDurableQueueRoles,
@Parameter(desc = "a comma-separated list of roles allowed to delete durable queues", name = "deleteDurableQueueRoles") String deleteDurableQueueRoles,
@Parameter(desc = "a comma-separated list of roles allowed to create non durable queues", name = "createNonDurableQueueRoles") String createNonDurableQueueRoles,
@Parameter(desc = "a comma-separated list of roles allowed to delete non durable queues", name = "deleteNonDurableQueueRoles") String deleteNonDurableQueueRoles,
@Parameter(desc = "a comma-separated list of roles allowed to send management messages messages", name = "manage") String manageRoles,
@Parameter(desc = "a comma-separated list of roles allowed to browse queues", name = "browse") String browseRoles,
@Parameter(desc = "a comma-separated list of roles allowed to create addresses", name = "createAddressRoles") String createAddressRoles,
@Parameter(desc = "a comma-separated list of roles allowed to delete addresses", name = "deleteAddressRoles") String deleteAddressRoles,
@Parameter(desc = "a comma-separated list of roles allowed to view management resources", name = "view") String viewRoles,
@Parameter(desc = "a comma-separated list of roles allowed to edit management resources", name = "edit") String editRoles) throws Exception;
@Operation(desc = "Remove security settings for an address", impact = MBeanOperationInfo.ACTION)
void removeSecuritySettings(@Parameter(desc = "an address match", name = "addressMatch") String addressMatch) throws Exception;

View File

@ -161,7 +161,12 @@ public final class ObjectNameBuilder {
return String.format("%s:broker=%s", domain, (jmxUseBrokerName && brokerName != null) ? ObjectName.quote(brokerName) : "artemis");
}
@Deprecated()
public ObjectName getManagementContextObjectName() throws Exception {
return ObjectName.getInstance(String.format("hawtio:type=security,area=jmx,name=ArtemisJMXSecurity"));
return getSecurityObjectName();
}
public ObjectName getSecurityObjectName() throws Exception {
return ObjectName.getInstance("hawtio:type=security,area=jmx,name=ArtemisJMXSecurity");
}
}

View File

@ -50,8 +50,13 @@ public class Role implements Serializable {
private boolean browse;
private boolean view;
private boolean edit;
public JsonObject toJson() {
return JsonLoader.createObjectBuilder().add("name", name).add("send", send).add("consume", consume).add("createDurableQueue", createDurableQueue).add("deleteDurableQueue", deleteDurableQueue).add("createNonDurableQueue", createNonDurableQueue).add("deleteNonDurableQueue", deleteNonDurableQueue).add("manage", manage).add("browse", browse).add("createAddress", createAddress).add("deleteAddress", deleteAddress).build();
return JsonLoader.createObjectBuilder().add("name", name).add("send", send).add("consume", consume).add("createDurableQueue", createDurableQueue).add("deleteDurableQueue", deleteDurableQueue).add("createNonDurableQueue", createNonDurableQueue).add("deleteNonDurableQueue", deleteNonDurableQueue).add("manage", manage)
.add("browse", browse).add("createAddress", createAddress).add("deleteAddress", deleteAddress).add("view", view).add("edit", edit).build();
}
public Role() {
@ -79,7 +84,7 @@ public class Role implements Serializable {
final boolean deleteNonDurableQueue,
final boolean manage) {
// This constructor exists for version compatibility on the API.
// it will pass the consume as a browse
// it will pass consume as browse
this(name, send, consume, createDurableQueue, deleteDurableQueue, createNonDurableQueue, deleteNonDurableQueue, manage, consume);
}
@ -95,7 +100,22 @@ public class Role implements Serializable {
final boolean browse) {
// This constructor exists for version compatibility on the API. If either createDurableQueue or createNonDurableQueue
// is true then createAddress will be true. If either deleteDurableQueue or deleteNonDurableQueue is true then deleteAddress will be true.
this(name, send, consume, createDurableQueue, deleteDurableQueue, createNonDurableQueue, deleteNonDurableQueue, manage, browse, createDurableQueue || createNonDurableQueue, deleteDurableQueue || deleteNonDurableQueue);
this(name, send, consume, createDurableQueue, deleteDurableQueue, createNonDurableQueue, deleteNonDurableQueue, manage, browse, createDurableQueue || createNonDurableQueue, deleteDurableQueue || deleteNonDurableQueue, false, false);
}
@Deprecated
public Role(final String name,
final boolean send,
final boolean consume,
final boolean createDurableQueue,
final boolean deleteDurableQueue,
final boolean createNonDurableQueue,
final boolean deleteNonDurableQueue,
final boolean manage,
final boolean browse,
final boolean createAddress,
final boolean deleteAddress) {
this(name, send, consume, createDurableQueue, deleteDurableQueue, createNonDurableQueue, deleteNonDurableQueue, manage, browse, createAddress, deleteAddress, false, false);
}
public Role(final String name,
@ -108,7 +128,9 @@ public class Role implements Serializable {
final boolean manage,
final boolean browse,
final boolean createAddress,
final boolean deleteAddress) {
final boolean deleteAddress,
final boolean view,
final boolean edit) {
if (name == null) {
throw new NullPointerException("name is null");
}
@ -123,6 +145,8 @@ public class Role implements Serializable {
this.deleteNonDurableQueue = deleteNonDurableQueue;
this.manage = manage;
this.browse = browse;
this.view = view;
this.edit = edit;
}
public String getName() {
@ -247,7 +271,12 @@ public class Role implements Serializable {
if (browse) {
stringReturn.append(" browse ");
}
if (view) {
stringReturn.append(" view ");
}
if (edit) {
stringReturn.append(" edit ");
}
stringReturn.append("]}");
return stringReturn.toString();
@ -297,6 +326,12 @@ public class Role implements Serializable {
if (!name.equals(role.name)) {
return false;
}
if (view != role.view) {
return false;
}
if (edit != role.edit) {
return false;
}
return true;
}
@ -315,6 +350,8 @@ public class Role implements Serializable {
result = 31 * result + (deleteNonDurableQueue ? 1 : 0);
result = 31 * result + (manage ? 1 : 0);
result = 31 * result + (browse ? 1 : 0);
result = 31 * result + (view ? 1 : 0);
result = 31 * result + (edit ? 1 : 0);
return result;
}
@ -329,5 +366,23 @@ public class Role implements Serializable {
deleteNonDurableQueue = deleteNonDurableQueue || other.deleteNonDurableQueue;
manage = manage || other.manage;
browse = browse || other.browse;
view = view || other.view;
edit = edit || other.edit;
}
public boolean isEdit() {
return edit;
}
public void setEdit(boolean edit) {
this.edit = edit;
}
public boolean isView() {
return view;
}
public void setView(boolean view) {
this.view = view;
}
}

View File

@ -61,7 +61,7 @@ public class SecurityFormatter {
Set<Role> roles = new HashSet<>(allRoles.size());
for (String role : allRoles) {
roles.add(new Role(role, send.contains(role), consume.contains(role), createDurableQueue.contains(role), deleteDurableQueue.contains(role), createNonDurableQueue.contains(role), deleteNonDurableQueue.contains(role), manageRoles.contains(role), browse.contains(role), createAddressRoles.contains(role), deleteAddressRoles.contains(role)));
roles.add(new Role(role, send.contains(role), consume.contains(role), createDurableQueue.contains(role), deleteDurableQueue.contains(role), createNonDurableQueue.contains(role), deleteNonDurableQueue.contains(role), manageRoles.contains(role), browse.contains(role), createAddressRoles.contains(role), deleteAddressRoles.contains(role), false, false));
}
return roles;
}

View File

@ -2,3 +2,4 @@ config=file:${karaf.etc}/artemis.xml
name=local
domain=karaf
rolePrincipalClass=org.apache.karaf.jaas.boot.principal.RolePrincipal
userPrincipalClass=org.apache.karaf.jaas.boot.principal.UserPrincipal

View File

@ -132,12 +132,19 @@
</execution>
</executions>
<configuration>
<file>${project.build.directory}/${project.build.finalName}/index.html</file>
<includes>
<include>${project.build.directory}/${project.build.finalName}/index.html</include>
<include>${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml</include>
</includes>
<replacements>
<replacement>
<token>&lt;title&gt;.*&lt;/title&gt;</token>
<value>&lt;title&gt;${project.name}&lt;/title&gt;</value>
</replacement>
<replacement>
<token>&lt;load-on-startup>1&lt;/load-on-startup></token>
<value>&lt;load-on-startup>-1&lt;/load-on-startup></value>
</replacement>
</replacements>
</configuration>
</plugin>

View File

@ -57,6 +57,7 @@ public class OsgiBroker {
private String name;
private String configurationUrl;
private String rolePrincipalClass;
private String userPrincipalClass;
private Map<String, ActiveMQComponent> components;
private Map<String, ServiceRegistration<?>> registrations;
private ServiceTracker tracker;
@ -69,11 +70,16 @@ public class OsgiBroker {
configurationUrl = getMandatory(properties, "config");
name = getMandatory(properties, "name");
rolePrincipalClass = (String) properties.get("rolePrincipalClass");
userPrincipalClass = (String) properties.get("userPrincipalClass");
String domain = getMandatory(properties, "domain");
ActiveMQJAASSecurityManager security = new ActiveMQJAASSecurityManager(domain);
if (rolePrincipalClass != null) {
security.setRolePrincipalClass(rolePrincipalClass);
}
if (userPrincipalClass != null) {
security.setUserPrincipalClass(userPrincipalClass);
}
String brokerInstance = null;
String artemisDataDir = System.getProperty("artemis.data");

View File

@ -1496,4 +1496,17 @@ public interface Configuration {
Configuration setLargeMessageSync(boolean largeMessageSync);
boolean isLargeMessageSync();
String getViewPermissionMethodMatchPattern();
void setViewPermissionMethodMatchPattern(String permissionMatchPattern);
boolean isManagementMessageRbac();
void setManagementMessageRbac(boolean val);
String getManagementRbacPrefix();
void setManagementRbacPrefix(String prefix);
}

View File

@ -434,6 +434,12 @@ public class ConfigurationImpl implements Configuration, Serializable {
private String literalMatchMarkers = ActiveMQDefaultConfiguration.getLiteralMatchMarkers();
private String viewPermissionMethodMatchPattern = ActiveMQDefaultConfiguration.getViewPermissionMethodMatchPattern();
private String managementRbacPrefix = ActiveMQDefaultConfiguration.getManagementRbacPrefix();
private boolean managementMessagesRbac = ActiveMQDefaultConfiguration.getManagementMessagesRbac();
/**
* Parent folder for all data folders.
*/
@ -441,7 +447,6 @@ public class ConfigurationImpl implements Configuration, Serializable {
private transient JsonObject jsonStatus = JsonLoader.createObjectBuilder().build();
private transient Checksum transientChecksum = null;
private JsonObject getJsonStatus() {
if (jsonStatus == null) {
jsonStatus = JsonLoader.createObjectBuilder().build();
@ -3308,6 +3313,36 @@ public class ConfigurationImpl implements Configuration, Serializable {
return largeMessageSync;
}
@Override
public String getViewPermissionMethodMatchPattern() {
return viewPermissionMethodMatchPattern;
}
@Override
public void setViewPermissionMethodMatchPattern(String permissionMatchPattern) {
viewPermissionMethodMatchPattern = permissionMatchPattern;
}
@Override
public boolean isManagementMessageRbac() {
return managementMessagesRbac;
}
@Override
public void setManagementMessageRbac(boolean val) {
this.managementMessagesRbac = val;
}
@Override
public String getManagementRbacPrefix() {
return managementRbacPrefix;
}
@Override
public void setManagementRbacPrefix(String prefix) {
this.managementRbacPrefix = prefix;
}
// extend property utils with ability to auto-fill and locate from collections
// collection entries are identified by the name() property
private static class CollectionAutoFillPropertiesUtil extends PropertyUtilsBean {

View File

@ -204,6 +204,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
private static final String DELETEADDRESS_NAME = "deleteAddress";
private static final String VIEW_NAME = "view";
private static final String EDIT_NAME = "edit";
// Address parsing
private static final String DEAD_LETTER_ADDRESS_NODE_NAME = "dead-letter-address";
@ -839,6 +843,12 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
config.setLargeMessageSync(getBoolean(e, "large-message-sync", config.isLargeMessageSync()));
config.setViewPermissionMethodMatchPattern(getString(e, "view-permission-method-match-pattern", config.getViewPermissionMethodMatchPattern(), NO_CHECK));
config.setManagementMessageRbac(getBoolean(e, "management-message-rbac", config.isManagementMessageRbac()));
config.setManagementRbacPrefix(getString(e, "management-rbac-prefix", config.getManagementRbacPrefix(), NO_CHECK));
parseAddressSettings(e, config);
parseResourceLimits(e, config);
@ -1151,6 +1161,8 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
ArrayList<String> browseRoles = new ArrayList<>();
ArrayList<String> createAddressRoles = new ArrayList<>();
ArrayList<String> deleteAddressRoles = new ArrayList<>();
ArrayList<String> viewRoles = new ArrayList<>();
ArrayList<String> editRoles = new ArrayList<>();
ArrayList<String> allRoles = new ArrayList<>();
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
@ -1187,6 +1199,10 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
createAddressRoles.add(role.trim());
} else if (DELETEADDRESS_NAME.equals(type)) {
deleteAddressRoles.add(role.trim());
} else if (VIEW_NAME.equals(type)) {
viewRoles.add(role.trim());
} else if (EDIT_NAME.equals(type)) {
editRoles.add(role.trim());
} else {
ActiveMQServerLogger.LOGGER.rolePermissionConfigurationError(type);
}
@ -1198,7 +1214,7 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
}
for (String role : allRoles) {
securityRoles.add(new Role(role, send.contains(role), consume.contains(role), createDurableQueue.contains(role), deleteDurableQueue.contains(role), createNonDurableQueue.contains(role), deleteNonDurableQueue.contains(role), manageRoles.contains(role), browseRoles.contains(role), createAddressRoles.contains(role), deleteAddressRoles.contains(role)));
securityRoles.add(new Role(role, send.contains(role), consume.contains(role), createDurableQueue.contains(role), deleteDurableQueue.contains(role), createNonDurableQueue.contains(role), deleteNonDurableQueue.contains(role), manageRoles.contains(role), browseRoles.contains(role), createAddressRoles.contains(role), deleteAddressRoles.contains(role), viewRoles.contains(role), editRoles.contains(role)));
}
return securityMatch;

View File

@ -2921,10 +2921,27 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
final String browseRoles,
final String createAddressRoles,
final String deleteAddressRoles) throws Exception {
addSecuritySettings(addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles, createAddressRoles, deleteAddressRoles, "", "");
}
@Override
public void addSecuritySettings(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,
final String viewRoles,
final String editRoles) throws Exception {
if (AuditLogger.isBaseLoggingEnabled()) {
AuditLogger.addSecuritySettings(this.server, addressMatch, sendRoles, consumeRoles, createDurableQueueRoles,
deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles,
browseRoles, createAddressRoles, deleteAddressRoles);
browseRoles, createAddressRoles, deleteAddressRoles, viewRoles, editRoles);
}
checkStarted();
@ -2934,7 +2951,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
server.getSecurityRepository().addMatch(addressMatch, roles);
PersistedSecuritySetting persistedRoles = new PersistedSecuritySetting(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, viewRoles, editRoles);
storageManager.storeSecuritySetting(persistedRoles);
} finally {
@ -2974,19 +2991,7 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
int i = 0;
for (Role role : roles) {
objRoles[i++] = new Object[]{
role.getName(),
CheckType.SEND.hasRole(role),
CheckType.CONSUME.hasRole(role),
CheckType.CREATE_DURABLE_QUEUE.hasRole(role),
CheckType.DELETE_DURABLE_QUEUE.hasRole(role),
CheckType.CREATE_NON_DURABLE_QUEUE.hasRole(role),
CheckType.DELETE_NON_DURABLE_QUEUE.hasRole(role),
CheckType.MANAGE.hasRole(role),
CheckType.BROWSE.hasRole(role),
CheckType.CREATE_ADDRESS.hasRole(role),
CheckType.DELETE_ADDRESS.hasRole(role)
};
objRoles[i++] = CheckType.asObjectArray(role);
}
return objRoles;
} finally {
@ -4668,5 +4673,9 @@ public class ActiveMQServerControlImpl extends AbstractControl implements Active
}
((SecurityStoreImpl)server.getSecurityStore()).invalidateAuthorizationCache();
}
public ActiveMQServer getServer() {
return server;
}
}

View File

@ -214,19 +214,7 @@ public class AddressControlImpl extends AbstractControl implements AddressContro
int i = 0;
for (Role role : roles) {
objRoles[i++] = new Object[]{
role.getName(),
CheckType.SEND.hasRole(role),
CheckType.CONSUME.hasRole(role),
CheckType.CREATE_DURABLE_QUEUE.hasRole(role),
CheckType.DELETE_DURABLE_QUEUE.hasRole(role),
CheckType.CREATE_NON_DURABLE_QUEUE.hasRole(role),
CheckType.DELETE_NON_DURABLE_QUEUE.hasRole(role),
CheckType.MANAGE.hasRole(role),
CheckType.BROWSE.hasRole(role),
CheckType.CREATE_ADDRESS.hasRole(role),
CheckType.DELETE_ADDRESS.hasRole(role)
};
objRoles[i++] = CheckType.asObjectArray(role);
}
return objRoles;
} finally {
@ -485,7 +473,7 @@ public class AddressControlImpl extends AbstractControl implements AddressContro
try {
return sendMessage(addressInfo.getName(), server, headers, type, body, durable, user, password, createMessageId);
} catch (Exception e) {
e.printStackTrace();
logger.debug("Failed to sendMessage", e);
throw new IllegalStateException(e.getMessage());
}
}

View File

@ -35,6 +35,8 @@ import org.apache.activemq.artemis.spi.core.remoting.ReadyListener;
public class ManagementRemotingConnection implements RemotingConnection {
Subject subject;
@Override
public Object getID() {
return "management";
@ -170,11 +172,12 @@ public class ManagementRemotingConnection implements RemotingConnection {
@Override
public void setSubject(Subject subject) {
this.subject = subject;
}
@Override
public Subject getSubject() {
return null;
return subject;
}
@Override

View File

@ -50,6 +50,10 @@ public class PersistedSecuritySetting implements EncodingSupport {
private SimpleString deleteAddressRoles;
private SimpleString viewRoles;
private SimpleString editRoles;
public PersistedSecuritySetting() {
}
@ -66,6 +70,8 @@ public class PersistedSecuritySetting implements EncodingSupport {
* @param browseRoles
* @param createAddressRoles
* @param deleteAddressRoles
* @param viewRoles
* @param editRoles
*/
public PersistedSecuritySetting(final String addressMatch,
final String sendRoles,
@ -77,7 +83,9 @@ public class PersistedSecuritySetting implements EncodingSupport {
final String manageRoles,
final String browseRoles,
final String createAddressRoles,
final String deleteAddressRoles) {
final String deleteAddressRoles,
final String viewRoles,
final String editRoles) {
super();
this.addressMatch = SimpleString.toSimpleString(addressMatch);
this.sendRoles = SimpleString.toSimpleString(sendRoles);
@ -90,6 +98,8 @@ public class PersistedSecuritySetting implements EncodingSupport {
this.browseRoles = SimpleString.toSimpleString(browseRoles);
this.createAddressRoles = SimpleString.toSimpleString(createAddressRoles);
this.deleteAddressRoles = SimpleString.toSimpleString(deleteAddressRoles);
this.viewRoles = SimpleString.toSimpleString(viewRoles);
this.editRoles = SimpleString.toSimpleString(editRoles);
}
@ -112,70 +122,83 @@ public class PersistedSecuritySetting implements EncodingSupport {
* @return the sendRoles
*/
public String getSendRoles() {
return sendRoles != null ? sendRoles.toString() : null;
return stringFrom(sendRoles);
}
/**
* @return the consumeRoles
*/
public String getConsumeRoles() {
return consumeRoles != null ? consumeRoles.toString() : null;
return stringFrom(consumeRoles);
}
/**
* @return the createDurableQueueRoles
*/
public String getCreateDurableQueueRoles() {
return createDurableQueueRoles != null ? createDurableQueueRoles.toString() : null;
return stringFrom(createDurableQueueRoles);
}
/**
* @return the deleteDurableQueueRoles
*/
public String getDeleteDurableQueueRoles() {
return deleteDurableQueueRoles != null ? deleteDurableQueueRoles.toString() : null;
return stringFrom(deleteDurableQueueRoles);
}
/**
* @return the createNonDurableQueueRoles
*/
public String getCreateNonDurableQueueRoles() {
return createNonDurableQueueRoles != null ? createNonDurableQueueRoles.toString() : null;
return stringFrom(createNonDurableQueueRoles);
}
/**
* @return the deleteNonDurableQueueRoles
*/
public String getDeleteNonDurableQueueRoles() {
return deleteNonDurableQueueRoles != null ? deleteNonDurableQueueRoles.toString() : null;
return stringFrom(deleteNonDurableQueueRoles);
}
/**
* @return the manageRoles
*/
public String getManageRoles() {
return manageRoles != null ? manageRoles.toString() : null;
return stringFrom(manageRoles);
}
/**
* @return the browseRoles
*/
public String getBrowseRoles() {
return browseRoles != null ? browseRoles.toString() : null;
return stringFrom(browseRoles);
}
/**
* @return the createAddressRoles
*/
public String getCreateAddressRoles() {
return createAddressRoles != null ? createAddressRoles.toString() : null;
return stringFrom(createAddressRoles);
}
/**
* @return the deleteAddressRoles
*/
public String getDeleteAddressRoles() {
return deleteAddressRoles != null ? deleteAddressRoles.toString() : null;
return stringFrom(deleteAddressRoles);
}
public String getViewRoles() {
return stringFrom(viewRoles);
}
public String getEditRoles() {
return stringFrom(editRoles);
}
private String stringFrom(SimpleString possiblyNullSimpleString) {
return possiblyNullSimpleString != null ? possiblyNullSimpleString.toString() : null;
}
@Override
@ -194,6 +217,8 @@ public class PersistedSecuritySetting implements EncodingSupport {
buffer.writeNullableSimpleString(browseRoles);
buffer.writeNullableSimpleString(createAddressRoles);
buffer.writeNullableSimpleString(deleteAddressRoles);
buffer.writeNullableSimpleString(viewRoles);
buffer.writeNullableSimpleString(editRoles);
}
@Override
@ -209,7 +234,9 @@ public class PersistedSecuritySetting implements EncodingSupport {
(manageRoles == null ? SIZE_NULL : SimpleString.sizeofNullableString(manageRoles)) +
(browseRoles == null ? SIZE_NULL : SimpleString.sizeofNullableString(browseRoles)) +
(createAddressRoles == null ? SIZE_NULL : SimpleString.sizeofNullableString(createAddressRoles)) +
(deleteAddressRoles == null ? SIZE_NULL : SimpleString.sizeofNullableString(deleteAddressRoles));
(deleteAddressRoles == null ? SIZE_NULL : SimpleString.sizeofNullableString(deleteAddressRoles)) +
(viewRoles == null ? SIZE_NULL : SimpleString.sizeofNullableString(viewRoles)) +
(editRoles == null ? SIZE_NULL : SimpleString.sizeofNullableString(editRoles));
}
@Override
@ -225,6 +252,8 @@ public class PersistedSecuritySetting implements EncodingSupport {
browseRoles = buffer.readNullableSimpleString();
createAddressRoles = buffer.readNullableSimpleString();
deleteAddressRoles = buffer.readNullableSimpleString();
viewRoles = buffer.readNullableSimpleString();
editRoles = buffer.readNullableSimpleString();
}
/* (non-Javadoc)
@ -245,6 +274,8 @@ public class PersistedSecuritySetting implements EncodingSupport {
result = prime * result + ((createAddressRoles == null) ? 0 : createAddressRoles.hashCode());
result = prime * result + ((deleteAddressRoles == null) ? 0 : deleteAddressRoles.hashCode());
result = prime * result + ((sendRoles == null) ? 0 : sendRoles.hashCode());
result = prime * result + ((viewRoles == null) ? 0 : viewRoles.hashCode());
result = prime * result + ((editRoles == null) ? 0 : editRoles.hashCode());
result = prime * result + (int) (storeId ^ (storeId >>> 32));
return result;
}
@ -316,6 +347,16 @@ public class PersistedSecuritySetting implements EncodingSupport {
return false;
} else if (!sendRoles.equals(other.sendRoles))
return false;
if (viewRoles == null) {
if (other.viewRoles != null)
return false;
} else if (!viewRoles.equals(other.viewRoles))
return false;
if (editRoles == null) {
if (other.editRoles != null)
return false;
} else if (!editRoles.equals(other.editRoles))
return false;
if (storeId != other.storeId)
return false;
return true;
@ -349,6 +390,9 @@ public class PersistedSecuritySetting implements EncodingSupport {
createAddressRoles +
", deleteAddressRoles=" +
deleteAddressRoles +
", viewRoles=" +
viewRoles +
", editRoles=" + editRoles +
"]";
}

View File

@ -76,7 +76,38 @@ public enum CheckType {
public boolean hasRole(final Role role) {
return role.isBrowse();
}
},
VIEW {
@Override
public boolean hasRole(final Role role) {
return role.isView();
}
},
EDIT {
@Override
public boolean hasRole(final Role role) {
return role.isEdit();
}
};
public static Object[] asObjectArray(Role role) {
// order is important!
return new Object[]{
role.getName(),
CheckType.SEND.hasRole(role),
CheckType.CONSUME.hasRole(role),
CheckType.CREATE_DURABLE_QUEUE.hasRole(role),
CheckType.DELETE_DURABLE_QUEUE.hasRole(role),
CheckType.CREATE_NON_DURABLE_QUEUE.hasRole(role),
CheckType.DELETE_NON_DURABLE_QUEUE.hasRole(role),
CheckType.MANAGE.hasRole(role),
CheckType.BROWSE.hasRole(role),
CheckType.CREATE_ADDRESS.hasRole(role),
CheckType.DELETE_ADDRESS.hasRole(role),
CheckType.VIEW.hasRole(role),
CheckType.EDIT.hasRole(role)
};
}
public abstract boolean hasRole(Role role);
}

View File

@ -46,7 +46,6 @@ import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager3;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager4;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager5;
import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException;
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
import org.apache.activemq.artemis.utils.CompositeAddress;
import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet;
import org.apache.activemq.artemis.utils.collections.TypedProperties;
@ -221,7 +220,11 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
connection.setSubject(subject);
}
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.userSuccesfullyAuthenticatedInAudit(subject, connection.getRemoteAddress(), connection.getID().toString());
if (connection != null) {
AuditLogger.userSuccesfullyAuthenticatedInAudit(subject, connection.getRemoteAddress(), connection.getID().toString());
} else {
AuditLogger.userSuccesfullyAuthenticatedInAudit(subject, null, null);
}
}
return validatedUser;
@ -303,7 +306,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
props.putSimpleStringProperty(ManagementHelper.HDR_ADDRESS, bareAddress);
props.putSimpleStringProperty(ManagementHelper.HDR_CHECK_TYPE, new SimpleString(checkType.toString()));
props.putSimpleStringProperty(ManagementHelper.HDR_USER, SimpleString.toSimpleString(user));
props.putSimpleStringProperty(ManagementHelper.HDR_USER, SimpleString.toSimpleString(getCaller(user, session.getRemotingConnection().getSubject())));
Notification notification = new Notification(null, CoreNotificationType.SECURITY_PERMISSION_VIOLATION, props);
@ -312,15 +315,23 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
Exception ex;
if (bareQueue == null) {
ex = ActiveMQMessageBundle.BUNDLE.userNoPermissions(session.getUsername(), checkType, bareAddress);
ex = ActiveMQMessageBundle.BUNDLE.userNoPermissions(getCaller(user, session.getRemotingConnection().getSubject()), checkType, bareAddress);
} else {
ex = ActiveMQMessageBundle.BUNDLE.userNoPermissionsQueue(session.getUsername(), checkType, bareQueue, bareAddress);
ex = ActiveMQMessageBundle.BUNDLE.userNoPermissionsQueue(getCaller(user, session.getRemotingConnection().getSubject()), checkType, bareQueue, bareAddress);
}
AuditLogger.securityFailure(session.getRemotingConnection().getSubject(), session.getRemotingConnection().getRemoteAddress(), ex.getMessage(), ex);
throw ex;
}
// if we get here we're granted, add to the cache
if (user == null) {
// should get all user/pass into a subject and only cache subjects
// till then when subject is in play, the user may be null and
// we cannot cache as we don't have a unique key
return;
}
ConcurrentHashSet<SimpleString> set;
String key = createAuthorizationCacheKey(user, checkType);
ConcurrentHashSet<SimpleString> act = getAuthorizationCacheEntry(key);
@ -340,19 +351,15 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
// we don't invalidate the authentication cache here because it's not necessary
}
public static String getUserFromSubject(Subject subject) {
if (subject == null) {
return null;
}
public String getUserFromSubject(Subject subject) {
return securityManager.getUserFromSubject(subject);
}
String validatedUser = "";
Set<UserPrincipal> users = subject.getPrincipals(UserPrincipal.class);
// should only ever be 1 UserPrincipal
for (UserPrincipal userPrincipal : users) {
validatedUser = userPrincipal.getName();
public String getCaller(String user, Subject subject) {
if (user != null) {
return user;
}
return validatedUser;
return getUserFromSubject(subject);
}
/**
@ -390,7 +397,7 @@ public class SecurityStoreImpl implements SecurityStore, HierarchicalRepositoryC
ActiveMQServerLogger.LOGGER.securityProblemWhileAuthenticating(e.getMessage());
if (AuditLogger.isResourceLoggingEnabled()) {
AuditLogger.userFailedAuthenticationInAudit(null, e.getMessage(), connection.getID().toString());
AuditLogger.userFailedAuthenticationInAudit(null, e.getMessage(), connection == null ? "null" : connection.getID().toString());
}
throw e;

View File

@ -463,7 +463,9 @@ public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
mapAdminToManage ? admin : false, // manage - map to admin based on configuration
read, // browse
admin, // createAddress
admin); // deleteAddress
admin, // deleteAddress
read, // view
write); // edit
if (existingRole != null) {
existingRole.merge(newRole);
} else {

View File

@ -2214,7 +2214,7 @@ public class ServerSessionImpl implements ServerSession, FailureListener {
throw e;
}
Message reply = managementService.handleMessage(message);
Message reply = managementService.handleMessage(this, message);
SimpleString replyTo = message.getReplyTo();

View File

@ -51,7 +51,7 @@ public class ArtemisMBeanServerBuilder extends MBeanServerBuilder {
private static final class MBeanInvocationHandler implements InvocationHandler {
private final MBeanServer wrapped;
private final List<String> guarded = Collections.unmodifiableList(Arrays.asList("invoke", "getAttribute", "getAttributes", "setAttribute", "setAttributes"));
private final List<String> guarded = Collections.unmodifiableList(Arrays.asList("invoke", "getAttribute", "getAttributes", "setAttribute", "setAttributes", "queryMBeans"));
MBeanInvocationHandler(MBeanServer mbeanServer) {
wrapped = mbeanServer;
@ -75,9 +75,6 @@ public class ArtemisMBeanServerBuilder extends MBeanServerBuilder {
}
guard.invoke(proxy, method, args);
}
if (method.getName().equals("queryMBeans")) {
guard.invoke(wrapped, method, args);
}
if (method.getName().equals("equals")
&& method.getParameterTypes().length == 1
&& method.getParameterTypes()[0] == Object.class) {

View File

@ -32,14 +32,13 @@ import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.security.auth.Subject;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.util.List;
public class ArtemisMBeanServerGuard implements InvocationHandler {
public class ArtemisMBeanServerGuard implements GuardInvocationHandler {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@ -124,6 +123,7 @@ public class ArtemisMBeanServerGuard implements InvocationHandler {
return jmxAccessControlList.isInAllowList(objectName);
}
@Override
public boolean canInvoke(String object, String operationName) {
ObjectName objectName = null;
try {

View File

@ -0,0 +1,368 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.Attribute;
import javax.management.AttributeList;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.security.auth.Subject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.core.management.impl.ActiveMQServerControlImpl;
import org.apache.activemq.artemis.core.management.impl.ManagementRemotingConnection;
import org.apache.activemq.artemis.core.security.CheckType;
import org.apache.activemq.artemis.core.security.SecurityAuth;
import org.apache.activemq.artemis.core.server.ActivateCallback;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.logs.AuditLogger;
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
public class ArtemisRbacInvocationHandler implements GuardInvocationHandler {
private final List<String> mBeanServerCheckedMethods = List.of("invoke", "getAttribute", "getAttributes", "setAttribute", "setAttributes", "queryMBeans", "queryNames");
private final List<String> uncheckedDomains = List.of("hawtio");
private final MBeanServer delegate;
private volatile ActiveMQServer activeMQServer;
String brokerDomain;
Pattern viewPermissionMatcher;
SimpleString rbacPrefix;
SimpleString mBeanServerRbacAddressPrefix;
ArtemisRbacInvocationHandler(MBeanServer mbeanServer) {
delegate = mbeanServer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
initAuditLoggerContext();
if (mBeanServerCheckedMethods.contains(method.getName())) {
if (activeMQServer != null) {
// happily initialised, do our check
securityCheck(method, args);
} else {
// initialisation pending registration of broker control with a check operation
if (method.getName().startsWith("query")) {
// restrict result in favour of an exception
return null;
}
if (!isUncheckedDomain(args)) {
throw new IllegalStateException("initialisation pending");
}
}
} else {
initializeFromFirstServerMBeanRegistration(method, args);
}
Object result;
try {
result = method.invoke(delegate, args);
} catch (InvocationTargetException ite) {
throw ite.getCause();
}
// filter query results based on RBAC
if (method.getName().startsWith("query") && result instanceof Collection<?>) {
((Collection<?>) result).removeIf(this::viewPermissionCheckFails);
}
return result;
}
private boolean isUncheckedDomain(Object[] args) {
final ObjectName objectName = objectNameFrom(args);
return isUncheckedDomain(objectName);
}
private boolean isUncheckedDomain(ObjectName objectName) {
if (objectName != null) {
return uncheckedDomains.contains(objectName.getDomain());
}
return false;
}
private ObjectName objectNameFrom(Object[] args) {
return (args != null && args.length > 0 && args[0] instanceof ObjectName) ? (ObjectName) args[0] : null;
}
@Override
public boolean canInvoke(String name, String operationName) {
boolean okInvoke = false;
try {
final ObjectName objectName = ObjectName.getInstance(name);
if (!isUncheckedDomain(objectName)) {
final SimpleString rbacAddress = addressFrom(objectName, operationName);
securityStoreCheck(rbacAddress, permissionFrom(operationName));
}
okInvoke = true;
} catch (Throwable expectedOnCheckFailOrInvalidObjectName) {
// denied
}
return okInvoke;
}
private void initializeFromFirstServerMBeanRegistration(Method method, Object[] args) {
if (activeMQServer == null && method.getName().equals("registerMBean")) {
if (args != null && args[0] instanceof ActiveMQServerControlImpl) {
activeMQServer = ((ActiveMQServerControlImpl) args[0]).getServer();
brokerDomain = activeMQServer.getConfiguration().getJMXDomain();
viewPermissionMatcher = Pattern.compile(activeMQServer.getConfiguration().getViewPermissionMethodMatchPattern());
rbacPrefix = SimpleString.toSimpleString(activeMQServer.getConfiguration().getManagementRbacPrefix());
mBeanServerRbacAddressPrefix = rbacPrefix.concat(".mbeanserver.");
((ActiveMQServerControlImpl) args[0]).getServer().registerActivateCallback(new ActivateCallback() {
@Override
public void shutdown(ActiveMQServer server) {
try {
activeMQServer.getManagementService().unregisterHawtioSecurity();
} catch (Exception bestEffortToTidyOnShutdown) {
}
activeMQServer = null;
}
});
try {
activeMQServer.getManagementService().registerHawtioSecurity(this);
} catch (Exception bestEffort) {
bestEffort.printStackTrace();
}
}
}
}
private void initAuditLoggerContext() {
//if this is invoked via jolokia the address will be set by the filter
//if not we can deduct it from RMI, or it must be internal
if (AuditLogger.isAnyLoggingEnabled() && AuditLogger.getRemoteAddress() == null) {
String url = "internal";
final String name = Thread.currentThread().getName();
if (name.startsWith("RMI TCP Connection")) {
url = name.substring(name.indexOf('-') + 1);
}
AuditLogger.setRemoteAddress(url);
}
}
// derive address to check from the method and args and then check relevant permission
void securityCheck(Method method, Object[] args) {
if (isUncheckedDomain(args)) {
return;
}
try {
final String methodName = method.getName();
if ("getAttribute".equals(methodName)) {
handleGetAttribute(delegate, (ObjectName) args[0], (String) args[1]);
} else if ("getAttributes".equals(methodName)) {
handleGetAttributes(delegate, (ObjectName) args[0], (String[]) args[1]);
} else if ("setAttribute".equals(methodName)) {
handleSetAttribute(delegate, (ObjectName) args[0], (Attribute) args[1]);
} else if ("setAttributes".equals(methodName)) {
handleSetAttributes(delegate, (ObjectName) args[0], (AttributeList) args[1]);
} else if ("invoke".equals(methodName)) {
handleInvoke((ObjectName) args[0], (String) args[1]);
} else if (method.getName().startsWith("query")) {
final SimpleString rbacAddress = mBeanServerRbacAddressPrefix.concat(methodName);
securityStoreCheck(rbacAddress, permissionFrom(methodName));
}
} catch (Exception e) {
throw new SecurityException(e.getMessage());
}
}
private void handleSetAttributes(MBeanServer delegate, ObjectName objectName, AttributeList attributeList) throws Exception {
for (Attribute attributeName : attributeList.asList()) {
handleSetAttribute(delegate, objectName, attributeName);
}
}
private void handleSetAttribute(MBeanServer delegate, ObjectName objectName, Attribute attributeName) throws Exception {
handleInvoke(objectName, "set" + attributeName);
}
private void handleGetAttributes(MBeanServer delegate, ObjectName objectName, String[] attributes) throws Exception {
for (String attribute : attributes) {
handleGetAttribute(delegate, objectName, attribute);
}
}
private void handleGetAttribute(MBeanServer delegate, ObjectName objectName, String attributeName) throws Exception {
MBeanInfo info = delegate.getMBeanInfo(objectName);
String prefix = "get";
for (MBeanAttributeInfo attr : info.getAttributes()) {
if (attr.getName().equals(attributeName)) {
prefix = attr.isIs() ? "is" : "get";
break;
}
}
handleInvoke(objectName, prefix + attributeName);
}
private void handleInvoke(ObjectName objectName, String operationName) throws Exception {
final SimpleString rbacAddress = addressFrom(objectName, operationName);
final CheckType permission = permissionFrom(operationName);
securityStoreCheck(rbacAddress, permission);
}
CheckType permissionFrom(String methodName) {
if (methodName != null && viewPermissionMatcher.matcher(methodName).matches()) {
return CheckType.VIEW;
}
return CheckType.EDIT;
}
String removeQuotes(String key) {
if (key != null && key.endsWith("\"")) {
return key.replace("\"", "");
}
return key;
}
SimpleString addressFrom(ObjectName objectName) {
return addressFrom(objectName, null);
}
// depends on ObjectNameBuilder impl that makes ObjectNames for control objects
// jmx.<domain><.type>[.component][.name][.operation]
SimpleString addressFrom(ObjectName objectName, String methodName) {
String name = removeQuotes(objectName.getKeyProperty("name"));
String component = removeQuotes(objectName.getKeyProperty("component"));
String type = null;
SimpleString rbacAddress = rbacPrefix;
if (brokerDomain.equals(objectName.getDomain())) {
if (component != null) {
if ("addresses".equals(component)) {
component = "address";
final String subComponent = objectName.getKeyProperty("subcomponent");
if ("diverts".equals(subComponent)) {
component = "divert";
} else if ("queues".equals(subComponent)) {
component = "queue";
}
name = removeQuotes(objectName.getKeyProperty(component));
}
} else {
// broker component, server control - identified by attribute with no component
final String brokerName = removeQuotes(objectName.getKeyProperty("broker"));
if (brokerName != null) {
component = "broker";
}
}
} else {
// non artemis broker domain, prefix with domain
rbacAddress = rbacAddress.concat('.').concat(objectName.getDomain());
type = removeQuotes(objectName.getKeyProperty("type"));
}
if (type != null) {
rbacAddress = rbacAddress.concat('.').concat(type);
}
if (component != null) {
rbacAddress = rbacAddress.concat('.').concat(component);
}
if (name != null) {
rbacAddress = rbacAddress.concat('.').concat(name);
}
if (methodName != null) {
rbacAddress = rbacAddress.concat('.').concat(methodName);
}
return rbacAddress;
}
private boolean viewPermissionCheckFails(Object candidate) {
boolean failed = false;
ObjectName objectName = candidate instanceof ObjectInstance ? ((ObjectInstance) candidate).getObjectName() : (ObjectName) candidate;
if (!isUncheckedDomain(objectName)) {
try {
final SimpleString rbacAddress = addressFrom(objectName);
securityStoreCheck(rbacAddress, CheckType.VIEW);
} catch (Exception checkFailed) {
failed = true;
}
}
return failed;
}
private void securityStoreCheck(SimpleString rbacAddress, CheckType checkType) throws Exception {
// use accessor as security store can be updated on config reload
activeMQServer.getSecurityStore().check(rbacAddress, checkType, delegateToAccessController);
}
// sufficiently empty to delegate to use of AccessController
// ideally AccessController should be the source of truth
private final SecurityAuth delegateToAccessController = new SecurityAuth() {
final ManagementRemotingConnection managementRemotingConnection = new ManagementRemotingConnection() {
@Override
public Subject getSubject() {
AccessControlContext accessControlContext = AccessController.getContext();
if (accessControlContext != null) {
return Subject.getSubject(accessControlContext);
}
return null;
}
};
@Override
public String getUsername() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public RemotingConnection getRemotingConnection() {
return managementRemotingConnection;
}
@Override
public String getSecurityDomain() {
return null;
}
};
}

View File

@ -0,0 +1,32 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.core.server.management;
import javax.management.MBeanServer;
import javax.management.MBeanServerBuilder;
import javax.management.MBeanServerDelegate;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public final class ArtemisRbacMBeanServerBuilder extends MBeanServerBuilder {
@Override
public MBeanServer newMBeanServer(String defaultDomain, MBeanServer outer, MBeanServerDelegate delegate) {
InvocationHandler handler = new ArtemisRbacInvocationHandler(super.newMBeanServer(defaultDomain, outer, delegate));
return (MBeanServer) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{MBeanServer.class}, handler);
}
}

View File

@ -0,0 +1,25 @@
/*
* 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 java.lang.reflect.InvocationHandler;
public interface GuardInvocationHandler extends InvocationHandler {
boolean canInvoke(String object, String operationName);
}

View File

@ -17,12 +17,8 @@
package org.apache.activemq.artemis.core.server.management;
import javax.management.NotCompliantMBeanException;
import org.apache.activemq.artemis.core.config.JMXConnectorConfiguration;
import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.server.ServiceComponent;
import org.apache.activemq.artemis.core.server.management.impl.HawtioSecurityControlImpl;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager;
public class ManagementContext implements ServiceComponent {
@ -31,16 +27,17 @@ public class ManagementContext implements ServiceComponent {
private JMXAccessControlList accessControlList;
private JMXConnectorConfiguration jmxConnectorConfiguration;
private ManagementConnector mBeanServer;
private ArtemisMBeanServerGuard guardHandler;
private GuardInvocationHandler guard;
private ActiveMQSecurityManager securityManager;
public void init() {
if (accessControlList != null) {
//if we are configured then assume we want to use the guard so set the system property
System.setProperty("javax.management.builder.initial", ArtemisMBeanServerBuilder.class.getCanonicalName());
guardHandler = new ArtemisMBeanServerGuard();
ArtemisMBeanServerGuard guardHandler = new ArtemisMBeanServerGuard();
guardHandler.setJMXAccessControlList(accessControlList);
ArtemisMBeanServerBuilder.setGuard(guardHandler);
guard = guardHandler;
}
}
@ -101,21 +98,8 @@ public class ManagementContext implements ServiceComponent {
this.jmxConnectorConfiguration = jmxConnectorConfiguration;
}
public JMXConnectorConfiguration getJmxConnectorConfiguration() {
return jmxConnectorConfiguration;
}
public HawtioSecurityControl getSecurityMBean(StorageManager storageManager) {
try {
return new HawtioSecurityControlImpl(guardHandler, storageManager);
} catch (NotCompliantMBeanException e) {
e.printStackTrace();
return null;
}
}
public ArtemisMBeanServerGuard getArtemisMBeanServerGuard() {
return guardHandler;
public GuardInvocationHandler getArtemisMBeanServerGuard() {
return guard;
}
public void setSecurityManager(ActiveMQSecurityManager securityManager) {

View File

@ -37,6 +37,7 @@ import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.postoffice.PostOffice;
import org.apache.activemq.artemis.core.remoting.server.RemotingService;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.security.SecurityAuth;
import org.apache.activemq.artemis.core.security.SecurityStore;
import org.apache.activemq.artemis.core.server.ActiveMQComponent;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
@ -137,13 +138,13 @@ public interface ManagementService extends NotificationService, ActiveMQComponen
Object[] getResources(Class<?> resourceType);
ICoreMessage handleMessage(Message message) throws Exception;
ICoreMessage handleMessage(SecurityAuth auth, Message message) throws Exception;
void registerHawtioSecurity(ArtemisMBeanServerGuard securityMBean) throws Exception;
void registerHawtioSecurity(GuardInvocationHandler guardInvocationHandler) throws Exception;
void unregisterHawtioSecurity() throws Exception;
Object getAttribute(String resourceName, String attribute);
Object getAttribute(String resourceName, String attribute, SecurityAuth auth);
Object invokeOperation(String resourceName, String operation, Object[] params) throws Exception;
Object invokeOperation(String resourceName, String operation, Object[] params, SecurityAuth auth) throws Exception;
}

View File

@ -19,7 +19,7 @@ package org.apache.activemq.artemis.core.server.management.impl;
import org.apache.activemq.artemis.core.management.impl.AbstractControl;
import org.apache.activemq.artemis.core.management.impl.MBeanInfoHelper;
import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.server.management.ArtemisMBeanServerGuard;
import org.apache.activemq.artemis.core.server.management.GuardInvocationHandler;
import org.apache.activemq.artemis.core.server.management.HawtioSecurityControl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -70,9 +70,9 @@ public class HawtioSecurityControlImpl extends AbstractControl implements Hawtio
* </ul>
*/
static final String[] CAN_INVOKE_RESULT_COLUMNS = SecurityMBeanOpenTypeInitializer.COLUMNS;
private final ArtemisMBeanServerGuard mBeanServerGuard;
private final GuardInvocationHandler mBeanServerGuard;
public HawtioSecurityControlImpl(ArtemisMBeanServerGuard mBeanServerGuard, StorageManager storageManager) throws NotCompliantMBeanException {
public HawtioSecurityControlImpl(GuardInvocationHandler mBeanServerGuard, StorageManager storageManager) throws NotCompliantMBeanException {
super(HawtioSecurityControl.class, storageManager);
this.mBeanServerGuard = mBeanServerGuard;
}

View File

@ -21,6 +21,7 @@ import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.NotificationBroadcasterSupport;
import javax.management.ObjectName;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
@ -31,6 +32,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Pattern;
import org.apache.activemq.artemis.api.core.BroadcastEndpointFactory;
import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration;
@ -48,8 +50,8 @@ import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl;
import org.apache.activemq.artemis.api.core.management.AddressControl;
import org.apache.activemq.artemis.api.core.management.BaseBroadcastGroupControl;
import org.apache.activemq.artemis.api.core.management.BridgeControl;
import org.apache.activemq.artemis.api.core.management.ConnectionRouterControl;
import org.apache.activemq.artemis.api.core.management.ClusterConnectionControl;
import org.apache.activemq.artemis.api.core.management.ConnectionRouterControl;
import org.apache.activemq.artemis.api.core.management.DivertControl;
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
@ -63,8 +65,8 @@ import org.apache.activemq.artemis.core.management.impl.AddressControlImpl;
import org.apache.activemq.artemis.core.management.impl.BaseBroadcastGroupControlImpl;
import org.apache.activemq.artemis.core.management.impl.BridgeControlImpl;
import org.apache.activemq.artemis.core.management.impl.BroadcastGroupControlImpl;
import org.apache.activemq.artemis.core.management.impl.ConnectionRouterControlImpl;
import org.apache.activemq.artemis.core.management.impl.ClusterConnectionControlImpl;
import org.apache.activemq.artemis.core.management.impl.ConnectionRouterControlImpl;
import org.apache.activemq.artemis.core.management.impl.DivertControlImpl;
import org.apache.activemq.artemis.core.management.impl.JGroupsChannelBroadcastGroupControlImpl;
import org.apache.activemq.artemis.core.management.impl.JGroupsFileBroadcastGroupControlImpl;
@ -77,7 +79,9 @@ import org.apache.activemq.artemis.core.paging.PagingManager;
import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.postoffice.PostOffice;
import org.apache.activemq.artemis.core.remoting.server.RemotingService;
import org.apache.activemq.artemis.core.security.CheckType;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.security.SecurityAuth;
import org.apache.activemq.artemis.core.security.SecurityStore;
import org.apache.activemq.artemis.core.server.ActiveMQMessageBundle;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
@ -85,13 +89,12 @@ import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
import org.apache.activemq.artemis.core.server.Divert;
import org.apache.activemq.artemis.core.server.Queue;
import org.apache.activemq.artemis.core.server.QueueFactory;
import org.apache.activemq.artemis.core.server.routing.ConnectionRouter;
import org.apache.activemq.artemis.core.server.cluster.Bridge;
import org.apache.activemq.artemis.core.server.cluster.BroadcastGroup;
import org.apache.activemq.artemis.core.server.cluster.ClusterConnection;
import org.apache.activemq.artemis.core.server.impl.AddressInfo;
import org.apache.activemq.artemis.core.server.impl.CleaningActivateCallback;
import org.apache.activemq.artemis.core.server.management.ArtemisMBeanServerGuard;
import org.apache.activemq.artemis.core.server.management.GuardInvocationHandler;
import org.apache.activemq.artemis.core.server.management.HawtioSecurityControl;
import org.apache.activemq.artemis.core.server.management.ManagementService;
import org.apache.activemq.artemis.core.server.management.Notification;
@ -100,6 +103,7 @@ import org.apache.activemq.artemis.core.server.metrics.AddressMetricNames;
import org.apache.activemq.artemis.core.server.metrics.BrokerMetricNames;
import org.apache.activemq.artemis.core.server.metrics.MetricsManager;
import org.apache.activemq.artemis.core.server.metrics.QueueMetricNames;
import org.apache.activemq.artemis.core.server.routing.ConnectionRouter;
import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
import org.apache.activemq.artemis.core.transaction.ResourceManager;
@ -108,7 +112,6 @@ import org.apache.activemq.artemis.utils.collections.ConcurrentHashSet;
import org.apache.activemq.artemis.utils.collections.TypedProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.invoke.MethodHandles;
import static org.apache.activemq.artemis.api.core.FilterConstants.NATIVE_MESSAGE_ID;
@ -156,6 +159,10 @@ public class ManagementServiceImpl implements ManagementService {
private final ObjectNameBuilder objectNameBuilder;
private final SimpleString managementMessageRbacResourceNamePrefix;
private final Pattern viewPermissionMatcher;
public ManagementServiceImpl(final MBeanServer mbeanServer, final Configuration configuration) {
this.mbeanServer = mbeanServer;
@ -168,6 +175,8 @@ public class ManagementServiceImpl implements ManagementService {
broadcaster = new NotificationBroadcasterSupport();
notificationsEnabled = true;
objectNameBuilder = ObjectNameBuilder.create(configuration.getJMXDomain(), configuration.getName(), configuration.isJMXUseBrokerName());
managementMessageRbacResourceNamePrefix = configuration.isManagementMessageRbac() ? SimpleString.toSimpleString(configuration.getManagementRbacPrefix()).concat('.') : null;
viewPermissionMatcher = Pattern.compile(configuration.getViewPermissionMethodMatchPattern());
}
@ -448,11 +457,8 @@ public class ManagementServiceImpl implements ManagementService {
} else {
control = new BaseBroadcastGroupControlImpl(broadcastGroup, storageManager, configuration);
}
//shouldnt ever be null
if (control != null) {
registerInJMX(objectName, control);
registerInRegistry(ResourceNames.BROADCAST_GROUP + configuration.getName(), control);
}
registerInJMX(objectName, control);
registerInRegistry(ResourceNames.BROADCAST_GROUP + configuration.getName(), control);
}
@Override
@ -512,22 +518,22 @@ public class ManagementServiceImpl implements ManagementService {
}
@Override
public void registerHawtioSecurity(ArtemisMBeanServerGuard mBeanServerGuard) throws Exception {
ObjectName objectName = objectNameBuilder.getManagementContextObjectName();
HawtioSecurityControl control = new HawtioSecurityControlImpl(mBeanServerGuard, storageManager);
public void registerHawtioSecurity(GuardInvocationHandler guard) throws Exception {
ObjectName objectName = objectNameBuilder.getSecurityObjectName();
HawtioSecurityControl control = new HawtioSecurityControlImpl(guard, storageManager);
registerInJMX(objectName, control);
registerInRegistry(ResourceNames.MANAGEMENT_SECURITY, control);
}
@Override
public void unregisterHawtioSecurity() throws Exception {
ObjectName objectName = objectNameBuilder.getManagementContextObjectName();
ObjectName objectName = objectNameBuilder.getSecurityObjectName();
unregisterFromJMX(objectName);
unregisterFromRegistry(ResourceNames.MANAGEMENT_SECURITY);
}
@Override
public ICoreMessage handleMessage(Message message) throws Exception {
public ICoreMessage handleMessage(SecurityAuth auth, Message message) throws Exception {
message = message.toCore();
// a reply message is sent with the result stored in the message body.
CoreMessage reply = new CoreMessage(storageManager.generateID(), 512);
@ -553,7 +559,7 @@ public class ManagementServiceImpl implements ManagementService {
}
try {
Object result = invokeOperation(resourceName, operation, params);
Object result = invokeOperation(resourceName, operation, params, auth);
ManagementHelper.storeResult(reply, result);
@ -574,7 +580,7 @@ public class ManagementServiceImpl implements ManagementService {
if (attribute != null) {
try {
Object result = getAttribute(resourceName, attribute);
Object result = getAttribute(resourceName, attribute, auth);
ManagementHelper.storeResult(reply, result);
@ -596,6 +602,21 @@ public class ManagementServiceImpl implements ManagementService {
return reply;
}
protected void securityCheck(String controlName, CheckType permission, SecurityAuth auth) throws Exception {
if (managementMessageRbacResourceNamePrefix == null) {
return;
}
final SimpleString address = managementMessageRbacResourceNamePrefix.concat(controlName);
securityStore.check(address, permission, auth);
}
protected CheckType permissionForInvoke(String method) {
if (viewPermissionMatcher.matcher(method).matches()) {
return CheckType.VIEW;
}
return CheckType.EDIT;
}
@Override
public synchronized Object getResource(final String resourceName) {
return registry.get(resourceName);
@ -772,8 +793,6 @@ public class ManagementServiceImpl implements ManagementService {
storageManager = null;
messagingServer = null;
registeredNames.clear();
started = false;
@ -849,7 +868,7 @@ public class ManagementServiceImpl implements ManagementService {
}
@Override
public Object getAttribute(final String resourceName, final String attribute) {
public Object getAttribute(final String resourceName, final String attribute, SecurityAuth auth) {
try {
Object resource = registry.get(resourceName);
@ -869,6 +888,11 @@ public class ManagementServiceImpl implements ManagementService {
throw ActiveMQMessageBundle.BUNDLE.noGetterMethod(attribute);
}
}
final String methodName = method.getName();
securityCheck(resourceName + "." + methodName, permissionForInvoke(methodName), auth);
return method.invoke(resource, new Object[0]);
} catch (Throwable t) {
throw new IllegalStateException("Problem while retrieving attribute " + attribute, t);
@ -877,14 +901,16 @@ public class ManagementServiceImpl implements ManagementService {
@Override
public Object invokeOperation(final String resourceName,
final String operation,
final Object[] params) throws Exception {
final String operation,
final Object[] params,
SecurityAuth auth) throws Exception {
Object resource = registry.get(resourceName);
if (resource == null) {
throw ActiveMQMessageBundle.BUNDLE.cannotFindResource(resourceName);
}
Method method = null;
Method[] methods = resource.getClass().getMethods();
@ -926,8 +952,10 @@ public class ManagementServiceImpl implements ManagementService {
throw ActiveMQMessageBundle.BUNDLE.noOperation(operation, params.length);
}
Object result = method.invoke(resource, params);
return result;
final String methodName = method.getName();
securityCheck(resourceName + "." + methodName, permissionForInvoke(methodName), auth);
return method.invoke(resource, params);
}
/**

View File

@ -60,11 +60,11 @@ public class LocalTarget extends AbstractTarget {
@Override
public <T> T getAttribute(String resourceName, String attributeName, Class<T> attributeClass, int timeout) throws Exception {
return (T)managementService.getAttribute(resourceName, attributeName);
return (T)managementService.getAttribute(resourceName, attributeName, null);
}
@Override
public <T> T invokeOperation(String resourceName, String operationName, Object[] operationParams, Class<T> operationClass, int timeout) throws Exception {
return (T)managementService.invokeOperation(resourceName, operationName, operationParams);
return (T)managementService.invokeOperation(resourceName, operationName, operationParams, null);
}
}

View File

@ -56,7 +56,6 @@ public class ActiveMQBasicSecurityManager implements ActiveMQSecurityManager5, U
public static final String BOOTSTRAP_ROLE_FILE = "bootstrapRoleFile";
private Map<String, String> properties;
private String rolePrincipalClass = RolePrincipal.class.getName();
private StorageManager storageManager;
@Override
@ -85,7 +84,7 @@ public class ActiveMQBasicSecurityManager implements ActiveMQSecurityManager5, U
Subject subject = new Subject();
subject.getPrincipals().add(new UserPrincipal(userToAuthenticate));
for (String role : getRole(userToAuthenticate).getRoles()) {
subject.getPrincipals().add((Principal) SecurityManagerUtil.createGroupPrincipal(role, rolePrincipalClass));
subject.getPrincipals().add((Principal) SecurityManagerUtil.createGroupPrincipal(role, RolePrincipal.class));
}
return subject;
}
@ -108,7 +107,7 @@ public class ActiveMQBasicSecurityManager implements ActiveMQSecurityManager5, U
final Set<Role> roles,
final CheckType checkType,
final String address) {
boolean authorized = SecurityManagerUtil.authorize(subject, roles, checkType, rolePrincipalClass);
boolean authorized = SecurityManagerUtil.authorize(subject, roles, checkType, RolePrincipal.class);
if (authorized) {
logger.trace("user is authorized");
} else {

View File

@ -20,6 +20,7 @@ import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.lang.invoke.MethodHandles;
import java.security.Principal;
import java.util.Set;
import org.apache.activemq.artemis.core.config.impl.SecurityConfiguration;
@ -29,6 +30,7 @@ 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.NoCacheLoginException;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -49,7 +51,8 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
private String certificateConfigurationName;
private SecurityConfiguration configuration;
private SecurityConfiguration certificateConfiguration;
private String rolePrincipalClass = "org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal";
private Class<? extends Principal> rolePrincipalClass = RolePrincipal.class;
private Class<? extends Principal> userPrincipalClass = UserPrincipal.class;
public ActiveMQJAASSecurityManager() {
}
@ -122,6 +125,11 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
return authorized;
}
@Override
public String getUserFromSubject(Subject subject) {
return SecurityManagerUtil.getUserFromSubject(subject, userPrincipalClass);
}
private Subject getAuthenticatedSubject(final String user,
final String password,
final RemotingConnection remotingConnection,
@ -182,10 +190,20 @@ public class ActiveMQJAASSecurityManager implements ActiveMQSecurityManager5 {
}
public String getRolePrincipalClass() {
return rolePrincipalClass;
return rolePrincipalClass.getName();
}
public void setRolePrincipalClass(String rolePrincipalClass) {
this.rolePrincipalClass = rolePrincipalClass;
@SuppressWarnings("unchecked")
public void setRolePrincipalClass(String principalClass) throws ClassNotFoundException {
this.rolePrincipalClass = (Class<? extends Principal>) Class.forName(principalClass);
}
public String getUserPrincipalClass() {
return userPrincipalClass.getName();
}
@SuppressWarnings("unchecked")
public void setUserPrincipalClass(String principalClass) throws ClassNotFoundException {
this.userPrincipalClass = (Class<? extends Principal>) Class.forName(principalClass);
}
}

View File

@ -16,11 +16,14 @@
*/
package org.apache.activemq.artemis.spi.core.security;
import javax.security.auth.Subject;
import java.util.Map;
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.spi.core.security.jaas.UserPrincipal;
import org.apache.activemq.artemis.utils.SecurityManagerUtil;
/**
* Use to validate whether a user has is valid to connect to the server and perform certain
@ -63,4 +66,8 @@ public interface ActiveMQSecurityManager {
default ActiveMQSecurityManager init(Map<String, String> properties) {
return this;
}
default String getUserFromSubject(Subject subject) {
return SecurityManagerUtil.getUserFromSubject(subject, UserPrincipal.class);
}
}

View File

@ -60,4 +60,5 @@ public interface ActiveMQSecurityManager5 extends ActiveMQSecurityManager {
* @return true if the user is authorized, else false
*/
boolean authorize(Subject subject, Set<Role> roles, CheckType checkType, String address);
}

View File

@ -33,7 +33,7 @@ public class SecurityManagerUtil {
private static final String WILDCARD = "*";
public static Set<RolePrincipal> getPrincipalsInRole(final CheckType checkType, final Set<Role> roles, final String rolePrincipalClass) {
public static Set<RolePrincipal> getPrincipalsInRole(final CheckType checkType, final Set<Role> roles, final Class rolePrincipalClass) {
Set principals = new HashSet<>();
for (Role role : roles) {
if (checkType.hasRole(role)) {
@ -47,7 +47,16 @@ public class SecurityManagerUtil {
return principals;
}
public static Object createGroupPrincipal(String name, String groupClass) throws Exception {
public static String getUserFromSubject(Subject subject, Class<? extends Principal> principalClass) {
if (subject != null) {
for (Principal candidate : subject.getPrincipals(principalClass)) {
return candidate.getName();
}
}
return null;
}
public static Object createGroupPrincipal(String name, Class cls) throws Exception {
if (WILDCARD.equals(name)) {
// simple match all group principal - match any name and class
return new Principal() {
@ -69,8 +78,6 @@ public class SecurityManagerUtil {
}
Object[] param = new Object[]{name};
Class<?> cls = Class.forName(groupClass);
Constructor<?>[] constructors = cls.getConstructors();
int i;
Object instance;
@ -83,7 +90,7 @@ public class SecurityManagerUtil {
if (i < constructors.length) {
instance = constructors[i].newInstance(param);
} else {
instance = cls.newInstance();
instance = cls.getDeclaredConstructor().newInstance();
Method[] methods = cls.getMethods();
i = 0;
for (i = 0; i < methods.length; i++) {
@ -106,7 +113,7 @@ public class SecurityManagerUtil {
/**
* 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) {
public static boolean authorize(final Subject subject, final Set<Role> roles, final CheckType checkType, final Class rolePrincipalClass) {
boolean authorized = false;
if (subject != null) {
@ -115,7 +122,7 @@ public class SecurityManagerUtil {
// Check the caller's roles
Set<Principal> rolesForSubject = new HashSet<>();
try {
rolesForSubject.addAll(subject.getPrincipals(Class.forName(rolePrincipalClass).asSubclass(Principal.class)));
rolesForSubject.addAll(subject.getPrincipals(rolePrincipalClass));
} catch (Exception e) {
ActiveMQServerLogger.LOGGER.failedToFindRolesForTheSubject(e);
}

View File

@ -935,6 +935,33 @@
</xsd:annotation>
</xsd:element>
<xsd:element name="view-permission-method-match-pattern" type="xsd:string" default="" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
The regular expression pattern to match management or JMX operations that require the 'view' permission
in your security-settings. Operations that don't match will default to the 'update' permission.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="management-message-rbac" type="xsd:boolean" default="false" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
Whether to apply RBAC based on security-settings for management operations when accessed through messages sent to the management address.
When set, the 'view' and 'update' permissions are applied to individual management operations based on the message contents. The `manage` permissions is still required to send the messages.
The security-settings match addresses will use the management-rbac-prefix prefix.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element name="management-rbac-prefix" type="xsd:string" default="mops" maxOccurs="1" minOccurs="0">
<xsd:annotation>
<xsd:documentation>
The prefix for security-settings match addresses that control RBAC for management operations. Only configure a value if the default causes a clash in your security settings match criteria.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
<xsd:element ref="security-settings" maxOccurs="1" minOccurs="0"/>
<xsd:element ref="broker-plugins" maxOccurs="1" minOccurs="0"/>

View File

@ -461,6 +461,42 @@ public class FileConfigurationParserTest extends ServerTestBase {
Assert.assertEquals("()", configuration.getLiteralMatchMarkers());
}
@Test
public void testViewPermissionMethodMatchPattern() throws Exception {
final String pattern = "^(get|list).+$";
String configStr = "<configuration><view-permission-method-match-pattern>" + pattern + "</view-permission-method-match-pattern>\n</configuration>\n";
FileConfigurationParser parser = new FileConfigurationParser();
ByteArrayInputStream input = new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8));
Configuration configuration = parser.parseMainConfig(input);
Assert.assertEquals(pattern, configuration.getViewPermissionMethodMatchPattern());
}
@Test
public void testManagementRbacPrefix() throws Exception {
final String pattern = "j.m.x";
String configStr = "<configuration><management-rbac-prefix>" + pattern + "</management-rbac-prefix>\n</configuration>\n";
FileConfigurationParser parser = new FileConfigurationParser();
ByteArrayInputStream input = new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8));
Configuration configuration = parser.parseMainConfig(input);
Assert.assertEquals(pattern, configuration.getManagementRbacPrefix());
}
@Test
public void testManagementRbac() throws Exception {
final boolean enabled = true;
String configStr = "<configuration><management-message-rbac>" + enabled + "</management-message-rbac>\n</configuration>\n";
FileConfigurationParser parser = new FileConfigurationParser();
ByteArrayInputStream input = new ByteArrayInputStream(configStr.getBytes(StandardCharsets.UTF_8));
Configuration configuration = parser.parseMainConfig(input);
Assert.assertEquals(enabled, configuration.isManagementMessageRbac());
}
// you should not use K, M notations on address settings max-size-messages
@Test
public void testExpectedErrorOverMaxMessageNotation() throws Exception {

View File

@ -700,35 +700,35 @@ public class FileConfigurationTest extends ConfigurationImplTest {
Set<Role> roles = securityRoles.get("#");
//cn=mygroup,dc=local,dc=com = amq1
Role testRole1 = new Role("cn=mygroup,dc=local,dc=com", false, false, false, false, true, false, false, false, false, false);
Role testRole1 = new Role("cn=mygroup,dc=local,dc=com", false, false, false, false, true, false, false, false, false, false, false, false);
//myrole1 = amq1 + amq2
Role testRole2 = new Role("myrole1", false, false, false, false, true, true, false, false, false, false);
Role testRole2 = new Role("myrole1", false, false, false, false, true, true, false, false, false, false, false, false);
//myrole3 = amq3 + amq4
Role testRole3 = new Role("myrole3", false, false, true, true, false, false, false, false, false, false);
Role testRole3 = new Role("myrole3", false, false, true, true, false, false, false, false, false, false, false, false);
//myrole4 = amq5 + amq!@#$%^&*() + amq6
Role testRole4 = new Role("myrole4", true, true, false, false, false, false, false, true, true, true);
Role testRole4 = new Role("myrole4", true, true, false, false, false, false, false, true, true, true, false, false);
//myrole5 = amq4 = amq3 + amq4
Role testRole5 = new Role("myrole5", false, false, true, true, false, false, false, false, false, false);
Role testRole5 = new Role("myrole5", false, false, true, true, false, false, false, false, false, false, false, false);
Role testRole6 = new Role("amq1", false, false, false, false, true, false, false, false, false, false);
Role testRole6 = new Role("amq1", false, false, false, false, true, false, false, false, false, false, false, false);
Role testRole7 = new Role("amq2", false, false, false, false, false, true, false, false, false, false);
Role testRole7 = new Role("amq2", false, false, false, false, false, true, false, false, false, false, false, false);
Role testRole8 = new Role("amq3", false, false, true, false, false, false, false, false, false, false);
Role testRole8 = new Role("amq3", false, false, true, false, false, false, false, false, false, false, false, false);
Role testRole9 = new Role("amq4", false, false, true, true, false, false, false, false, false, false);
Role testRole9 = new Role("amq4", false, false, true, true, false, false, false, false, false, false, false, false);
Role testRole10 = new Role("amq5", false, false, false, false, false, false, false, false, true, true);
Role testRole10 = new Role("amq5", false, false, false, false, false, false, false, false, true, true, false, false);
Role testRole11 = new Role("amq6", false, true, false, false, false, false, false, true, false, false);
Role testRole11 = new Role("amq6", false, true, false, false, false, false, false, true, false, false, false, false);
Role testRole12 = new Role("amq7", false, false, false, false, false, false, true, false, false, false);
Role testRole12 = new Role("amq7", false, false, false, false, false, false, true, false, false, false, false, false);
Role testRole13 = new Role("amq!@#$%^&*()", true, false, false, false, false, false, false, false, false, false);
Role testRole13 = new Role("amq!@#$%^&*()", true, false, false, false, false, false, false, false, false, false, false, false);
assertEquals(13, roles.size());
assertTrue(roles.contains(testRole1));

View File

@ -35,7 +35,7 @@ public class RoleTest extends Assert {
@Test
public void testWriteRole() throws Exception {
Role role = new Role("testWriteRole", true, false, false, false, false, false, false, false, false, false);
Role role = new Role("testWriteRole", true, false, false, false, false, false, false, false, false, false, false, false);
Assert.assertTrue(SEND.hasRole(role));
Assert.assertFalse(CONSUME.hasRole(role));
Assert.assertFalse(CREATE_DURABLE_QUEUE.hasRole(role));
@ -49,7 +49,7 @@ public class RoleTest extends Assert {
@Test
public void testReadRole() throws Exception {
Role role = new Role("testReadRole", false, true, false, false, false, false, false, true, false, false);
Role role = new Role("testReadRole", false, true, false, false, false, false, false, true, false, false, false, false);
Assert.assertFalse(SEND.hasRole(role));
Assert.assertTrue(CONSUME.hasRole(role));
Assert.assertFalse(CREATE_DURABLE_QUEUE.hasRole(role));
@ -63,7 +63,7 @@ public class RoleTest extends Assert {
@Test
public void testCreateRole() throws Exception {
Role role = new Role("testCreateRole", false, false, true, false, false, false, false, false, false, false);
Role role = new Role("testCreateRole", false, false, true, false, false, false, false, false, false, false, false, false);
Assert.assertFalse(SEND.hasRole(role));
Assert.assertFalse(CONSUME.hasRole(role));
Assert.assertTrue(CREATE_DURABLE_QUEUE.hasRole(role));
@ -77,7 +77,7 @@ public class RoleTest extends Assert {
@Test
public void testManageRole() throws Exception {
Role role = new Role("testManageRole", false, false, false, false, false, false, true, false, false, false);
Role role = new Role("testManageRole", false, false, false, false, false, false, true, false, false, false, false, false);
Assert.assertFalse(SEND.hasRole(role));
Assert.assertFalse(CONSUME.hasRole(role));
Assert.assertFalse(CREATE_DURABLE_QUEUE.hasRole(role));
@ -91,12 +91,15 @@ public class RoleTest extends Assert {
@Test
public void testEqualsAndHashcode() throws Exception {
Role role = new Role("testEquals", true, true, true, false, false, false, false, false, false, false);
Role sameRole = new Role("testEquals", true, true, true, false, false, false, false, false, false, false);
Role roleWithDifferentName = new Role("notEquals", true, true, true, false, false, false, false, false, false, false);
Role roleWithDifferentRead = new Role("testEquals", false, true, true, false, false, false, false, false, false, false);
Role roleWithDifferentWrite = new Role("testEquals", true, false, true, false, false, false, false, false, false, false);
Role roleWithDifferentCreate = new Role("testEquals", true, true, false, false, false, false, false, false, false, false);
Role role = new Role("testEquals", true, true, true, false, false, false, false, false, false, false, false, false);
Role sameRole = new Role("testEquals", true, true, true, false, false, false, false, false, false, false, false, false);
Role roleWithDifferentName = new Role("notEquals", true, true, true, false, false, false, false, false, false, false, false, false);
Role roleWithDifferentRead = new Role("testEquals", false, true, true, false, false, false, false, false, false, false, false, false);
Role roleWithDifferentWrite = new Role("testEquals", true, false, true, false, false, false, false, false, false, false, false, false);
Role roleWithDifferentCreate = new Role("testEquals", true, true, false, false, false, false, false, false, false, false, false, false);
Role roleWithDifferentView = new Role("testEquals", true, true, true, false, false, false, false, false, false, false, true, false);
Role roleWithDifferentUpdate = new Role("testEquals", true, true, true, false, false, false, false, false, false, false, false, true);
Assert.assertTrue(role.equals(role));
@ -115,6 +118,12 @@ public class RoleTest extends Assert {
Assert.assertFalse(role.equals(roleWithDifferentCreate));
Assert.assertFalse(role.hashCode() == roleWithDifferentCreate.hashCode());
Assert.assertFalse(role.equals(roleWithDifferentView));
Assert.assertFalse(role.hashCode() == roleWithDifferentView.hashCode());
Assert.assertFalse(role.equals(roleWithDifferentUpdate));
Assert.assertFalse(role.hashCode() == roleWithDifferentUpdate.hashCode());
Assert.assertFalse(role.equals(null));
}

View File

@ -25,7 +25,8 @@ import org.apache.activemq.artemis.core.security.SecurityAuth;
import org.apache.activemq.artemis.core.settings.impl.HierarchicalObjectRepository;
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager5;
import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException;
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.RandomUtil;
import org.junit.Test;
@ -34,35 +35,39 @@ import static org.junit.Assert.assertNull;
public class SecurityStoreImplTest {
final ActiveMQSecurityManager5 securityManager = new ActiveMQSecurityManager5() {
@Override
public Subject authenticate(String user,
String password,
RemotingConnection remotingConnection,
String securityDomain) {
Subject subject = new Subject();
subject.getPrincipals().add(new UserPrincipal(user));
return subject;
}
@Override
public boolean authorize(Subject subject, Set<Role> roles, CheckType checkType, String address) {
return true;
}
@Override
public boolean validateUser(String user, String password) {
return false;
}
@Override
public boolean validateUserAndRole(String user, String password, Set<Role> roles, CheckType checkType) {
return false;
}
};
@Test
public void zeroCacheSizeTest() throws Exception {
ActiveMQSecurityManager5 securityManager = new ActiveMQSecurityManager5() {
@Override
public Subject authenticate(String user,
String password,
RemotingConnection remotingConnection,
String securityDomain) throws NoCacheLoginException {
return new Subject();
}
@Override
public boolean authorize(Subject subject, Set<Role> roles, CheckType checkType, String address) {
return true;
}
@Override
public boolean validateUser(String user, String password) {
return false;
}
@Override
public boolean validateUserAndRole(String user, String password, Set<Role> roles, CheckType checkType) {
return false;
}
};
final String user = RandomUtil.randomString();
SecurityStoreImpl securityStore = new SecurityStoreImpl(new HierarchicalObjectRepository<>(), securityManager, 999, true, "", null, null, 0, 0);
assertNull(securityStore.getAuthenticationCache());
securityStore.authenticate(RandomUtil.randomString(), RandomUtil.randomString(), null);
assertEquals(user, securityStore.authenticate(user, RandomUtil.randomString(), null));
assertEquals(0, securityStore.getAuthenticationCacheSize());
securityStore.invalidateAuthenticationCache(); // ensure this doesn't throw an NPE
@ -91,4 +96,21 @@ public class SecurityStoreImplTest {
assertEquals(0, securityStore.getAuthorizationCacheSize());
securityStore.invalidateAuthorizationCache(); // ensure this doesn't throw an NPE
}
@Test
public void getCaller() throws Exception {
SecurityStoreImpl securityStore = new SecurityStoreImpl(new HierarchicalObjectRepository<>(), securityManager, 999, true, "", null, null, 0, 0);
assertNull(securityStore.getCaller(null, null));
assertEquals("joe", securityStore.getCaller("joe", null));
Subject subject = new Subject();
assertEquals("joe", securityStore.getCaller("joe", subject));
subject.getPrincipals().add(new RolePrincipal("r"));
assertEquals("joe", securityStore.getCaller("joe", subject));
assertNull(securityStore.getCaller(null, subject));
subject.getPrincipals().add(new UserPrincipal("u"));
assertEquals("u", securityStore.getCaller(null, subject));
assertEquals("joe", securityStore.getCaller("joe", subject));
}
}

View File

@ -30,7 +30,6 @@ 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.security.impl.SecurityStoreImpl;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.junit.Rule;
import org.junit.Test;
@ -88,9 +87,9 @@ public class JAASSecurityManagerClassLoadingTest {
Subject result = securityManager.authenticate("first", "secret", null, null);
assertNotNull(result);
assertEquals("first", SecurityStoreImpl.getUserFromSubject(result));
assertEquals("first", securityManager.getUserFromSubject(result));
Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
boolean authorizationResult = securityManager.authorize(result, roles, CheckType.SEND, "someaddress");

View File

@ -40,17 +40,18 @@ import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.postoffice.PostOffice;
import org.apache.activemq.artemis.core.remoting.server.RemotingService;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.security.SecurityAuth;
import org.apache.activemq.artemis.core.security.SecurityStore;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.Divert;
import org.apache.activemq.artemis.core.server.Queue;
import org.apache.activemq.artemis.core.server.QueueFactory;
import org.apache.activemq.artemis.core.server.management.GuardInvocationHandler;
import org.apache.activemq.artemis.core.server.routing.ConnectionRouter;
import org.apache.activemq.artemis.core.server.cluster.Bridge;
import org.apache.activemq.artemis.core.server.cluster.BroadcastGroup;
import org.apache.activemq.artemis.core.server.cluster.ClusterConnection;
import org.apache.activemq.artemis.core.server.impl.AddressInfo;
import org.apache.activemq.artemis.core.server.management.ArtemisMBeanServerGuard;
import org.apache.activemq.artemis.core.server.management.ManagementService;
import org.apache.activemq.artemis.core.server.management.Notification;
import org.apache.activemq.artemis.core.server.management.NotificationListener;
@ -351,12 +352,12 @@ public class ClusteredResetMockTest extends ServerTestBase {
}
@Override
public ICoreMessage handleMessage(Message message) throws Exception {
public ICoreMessage handleMessage(SecurityAuth auth, Message message) throws Exception {
return null;
}
@Override
public void registerHawtioSecurity(ArtemisMBeanServerGuard securityMBean) throws Exception {
public void registerHawtioSecurity(GuardInvocationHandler securityMBean) throws Exception {
}
@ -366,12 +367,12 @@ public class ClusteredResetMockTest extends ServerTestBase {
}
@Override
public Object getAttribute(String resourceName, String attribute) {
public Object getAttribute(String resourceName, String attribute, SecurityAuth auth) {
return null;
}
@Override
public Object invokeOperation(String resourceName, String operation, Object[] params) throws Exception {
public Object invokeOperation(String resourceName, String operation, Object[] params, SecurityAuth auth) throws Exception {
return null;
}

View File

@ -0,0 +1,693 @@
/*
* 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.JMX;
import javax.management.MBeanServer;
import javax.management.MBeanServerDelegate;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;
import javax.security.auth.Subject;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl;
import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
import org.apache.activemq.artemis.core.security.CheckType;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
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.tests.util.ServerTestBase;
import org.apache.activemq.artemis.utils.RandomUtil;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class ArtemisRbacMBeanServerBuilderTest extends ServerTestBase {
MBeanServer mbeanServer;
MBeanServerDelegate mBeanServerDelegate;
ArtemisRbacMBeanServerBuilder underTest;
@Before
public void setUnderTest() throws Exception {
underTest = new ArtemisRbacMBeanServerBuilder();
mbeanServer = Mockito.mock(MBeanServer.class);
mBeanServerDelegate = Mockito.mock(MBeanServerDelegate.class);
}
@Test
public void testRbacAddressFrom() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
ArtemisRbacInvocationHandler handler = (ArtemisRbacInvocationHandler) Proxy.getInvocationHandler(proxy);
handler.brokerDomain = "a.b";
handler.rbacPrefix = SimpleString.toSimpleString("jmx");
try {
handler.addressFrom(null);
fail("expect exception");
} catch (NullPointerException expected) {
}
SimpleString rbacAddress = handler.addressFrom(new ObjectName("java.lang", "type", "Runtime"));
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx.java.lang.Runtime")));
rbacAddress = handler.addressFrom(new ObjectName("a.b", "type", "Runtime"));
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx")));
rbacAddress = handler.addressFrom(new ObjectName("a.b", "broker", "bb"));
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx.broker")));
Hashtable<String, String> attrs = new Hashtable<>();
attrs.put("broker", "bb");
attrs.put("type", "t");
attrs.put("component", "c");
attrs.put("name", "n");
rbacAddress = handler.addressFrom(new ObjectName("a.b", attrs));
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx.c.n")));
rbacAddress = handler.addressFrom(new ObjectName("a.b", attrs), "doIt");
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx.c.n.doIt")));
// non broker domain
rbacAddress = handler.addressFrom(new ObjectName("j.l", attrs));
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx.j.l.t.c.n")));
// address
attrs.clear();
attrs.put("broker", "bb");
attrs.put("address", "a");
attrs.put("component", "addresses");
rbacAddress = handler.addressFrom(new ObjectName("a.b", attrs), "opOnA");
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx.address.a.opOnA")));
// queue
attrs.clear();
attrs.put("broker", "bb");
attrs.put("address", "a");
attrs.put("queue", "q");
attrs.put("component", "addresses");
attrs.put("subcomponent", "queues");
rbacAddress = handler.addressFrom(new ObjectName("a.b", attrs), "opOnQ");
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx.queue.q.opOnQ")));
// divert
attrs.clear();
attrs.put("broker", "bb");
attrs.put("address", "a");
attrs.put("queue", "q");
attrs.put("component", "addresses");
attrs.put("subcomponent", "diverts");
attrs.put("divert", "d");
rbacAddress = handler.addressFrom(new ObjectName("a.b", attrs), "opOnDivert");
assertNotNull(rbacAddress);
assertEquals(0, rbacAddress.compareTo(SimpleString.toSimpleString("jmx.divert.d.opOnDivert")));
}
@Test
public void testRbacAddressFromWithObjectNameBuilder() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
ArtemisRbacInvocationHandler handler =
(ArtemisRbacInvocationHandler) Proxy.getInvocationHandler(proxy);
handler.brokerDomain = ActiveMQDefaultConfiguration.getDefaultJmxDomain();
handler.rbacPrefix = SimpleString.toSimpleString(ActiveMQDefaultConfiguration.getManagementRbacPrefix());
for (Method m : ObjectNameBuilder.class.getDeclaredMethods() ) {
if (Modifier.isPublic(m.getModifiers()) && ObjectName.class == m.getReturnType()) {
Object[] args = new Object[m.getParameterCount()];
for (int i = 0; i < args.length; i++) {
Class<?> type = m.getParameterTypes()[i];
if (type == String.class) {
args[i] = RandomUtil.randomString();
} else if (SimpleString.class == type) {
args[i] = RandomUtil.randomSimpleString();
} else if (RoutingType.class == type) {
args[i] = RoutingType.ANYCAST;
}
}
assertNotNull(handler.addressFrom((ObjectName) m.invoke(ObjectNameBuilder.DEFAULT, args), m.getName()));
}
}
}
@Test
public void testPermissionsFromDefault() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
ArtemisRbacInvocationHandler handler = (ArtemisRbacInvocationHandler) Proxy.getInvocationHandler(proxy);
handler.viewPermissionMatcher = Pattern.compile(ActiveMQDefaultConfiguration.getViewPermissionMethodMatchPattern());
assertEquals(CheckType.VIEW, handler.permissionFrom("getClass"));
assertEquals(CheckType.VIEW, handler.permissionFrom("listDeliveringMessages"));
assertEquals(CheckType.VIEW, handler.permissionFrom("isEmpty"));
assertEquals(CheckType.EDIT, handler.permissionFrom("quote"));
assertEquals(CheckType.VIEW, handler.permissionFrom("getA"));
assertEquals(CheckType.EDIT, handler.permissionFrom("setA"));
}
@Test
public void testPermissionsFromCustom() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
ArtemisRbacInvocationHandler handler = (ArtemisRbacInvocationHandler) Proxy.getInvocationHandler(proxy);
handler.viewPermissionMatcher = Pattern.compile("^(is(?!SecurityEnabled)|get|list|query).*$");
assertEquals(CheckType.EDIT, handler.permissionFrom("isSecurityEnabled"));
assertEquals(CheckType.VIEW, handler.permissionFrom("isEmpty"));
assertEquals(CheckType.VIEW, handler.permissionFrom("getClass"));
assertEquals(CheckType.VIEW, handler.permissionFrom("listDeliveringMessages"));
assertEquals(CheckType.EDIT, handler.permissionFrom("quote"));
assertEquals(CheckType.VIEW, handler.permissionFrom("getA"));
assertEquals(CheckType.EDIT, handler.permissionFrom("setA"));
}
@Test(expected = IllegalStateException.class)
public void testUninitialised() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
ObjectName runtimeName = new ObjectName("java.lang", "type", "Runtime");
RuntimeMXBean runtime = JMX.newMBeanProxy(
proxy, runtimeName, RuntimeMXBean.class, false);
runtime.getVmVersion();
}
@Test(expected = UndeclaredThrowableException.class)
public void testUnCheckedDomain() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
ObjectName unchecked = new ObjectName("hawtio", "type", "Runtime");
RuntimeMXBean runtime = JMX.newMBeanProxy(
proxy, unchecked, RuntimeMXBean.class, false);
runtime.getVmVersion();
}
@Test(expected = SecurityException.class)
public void testNotLoggedIn() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true);
server.start();
ObjectName runtimeName = new ObjectName("java.lang", "type", "Runtime");
RuntimeMXBean runtime = JMX.newMBeanProxy(
proxy, runtimeName, RuntimeMXBean.class, false);
runtime.getVmVersion();
}
@Test(expected = SecurityException.class)
public void testNoPermission() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true);
server.start();
ObjectName runtimeName = new ObjectName("java.lang", "type", "Runtime");
final RuntimeMXBean runtime = JMX.newMBeanProxy(
proxy, runtimeName, RuntimeMXBean.class, false);
Subject viewSubject = new Subject();
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
throw Subject.doAs(viewSubject, (PrivilegedExceptionAction<Exception>) () -> {
try {
runtime.getVmVersion();
return null;
} catch (Exception e1) {
return e1;
}
});
}
@Test
public void testPermissionGetAttr() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true);
Set<Role> roles = new HashSet<>();
roles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("mops.broker.getCurrentTimeMillis", roles);
server.start();
ObjectName objectName = ObjectNameBuilder.DEFAULT.getActiveMQServerObjectName();
final ActiveMQServerControl serverControl = JMX.newMBeanProxy(
proxy, objectName, ActiveMQServerControl.class, false);
Subject viewSubject = new Subject();
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
Object ret = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.getCurrentTimeMillis();
} catch (Exception e1) {
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof Long);
// verify failure case
Subject noPermSubject = new Subject();
noPermSubject.getPrincipals().add(new UserPrincipal("dud"));
noPermSubject.getPrincipals().add(new RolePrincipal("dud"));
ret = Subject.doAs(noPermSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.getCurrentTimeMillis();
} catch (Exception e1) {
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof SecurityException);
}
@Test
public void testPermissionIsAttr() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true).setManagementRbacPrefix("jmx");
Set<Role> roles = new HashSet<>();
roles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("jmx.broker.isSecurityEnabled", roles);
server.start();
final ActiveMQServerControl serverControl = JMX.newMBeanProxy(
proxy, ObjectNameBuilder.DEFAULT.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
Subject viewSubject = new Subject();
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
Object ret = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.isSecurityEnabled();
} catch (Exception e1) {
e1.printStackTrace();
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof Boolean);
// verify failure case
Subject noPermSubject = new Subject();
noPermSubject.getPrincipals().add(new UserPrincipal("dud"));
noPermSubject.getPrincipals().add(new RolePrincipal("dud"));
ret = Subject.doAs(noPermSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.isSecurityEnabled();
} catch (Exception e1) {
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof SecurityException);
}
@Test
public void testPermissionWithConfiguredJmxPrefix() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true).setManagementRbacPrefix("j.m.x");
Set<Role> roles = new HashSet<>();
roles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("j.m.x.broker.*", roles);
server.start();
final ActiveMQServerControl serverControl = JMX.newMBeanProxy(
proxy, ObjectNameBuilder.DEFAULT.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
Subject viewSubject = new Subject();
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
Object ret = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.isSecurityEnabled();
} catch (Exception e1) {
e1.printStackTrace();
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof Boolean);
}
@Test
public void testConfigViewMethodMatchNoPermission() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true);
// isSecurityEnabled will require Update permission
server.getConfiguration().setViewPermissionMethodMatchPattern("^(is(?!SecurityEnabled)|get|list|query).*$");
Set<Role> roles = new HashSet<>();
roles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("mops.broker.#", roles);
server.start();
final ActiveMQServerControl serverControl = JMX.newMBeanProxy(
proxy, ObjectNameBuilder.DEFAULT.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
Subject viewSubject = new Subject();
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
Object ret = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.isSecurityEnabled();
} catch (Exception e1) {
e1.printStackTrace();
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof SecurityException);
assertTrue(((Exception)ret).getMessage().contains("EDIT"));
// another `is` op is ok with view
ret = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.isActive();
} catch (Exception e1) {
e1.printStackTrace();
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof Boolean);
}
@Test
public void testConfigMethodMatchEmptyNeedsUpdate() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true).setManagementRbacPrefix("jmx");
// all ops will require Update permission
server.getConfiguration().setViewPermissionMethodMatchPattern("");
Set<Role> viewRoles = new HashSet<>();
viewRoles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
Set<Role> editRoles = new HashSet<>();
editRoles.add(new Role("updaters", false, false, false, false, false, false, false, false, false, false, false, true));
server.getConfiguration().putSecurityRoles("jmx.broker.#", viewRoles);
server.getConfiguration().putSecurityRoles("jmx.broker.isSecurityEnabled", editRoles);
server.start();
final ActiveMQServerControl serverControl = JMX.newMBeanProxy(
proxy, ObjectNameBuilder.DEFAULT.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
Subject testSubject = new Subject();
testSubject.getPrincipals().add(new UserPrincipal("v"));
testSubject.getPrincipals().add(new RolePrincipal("viewers"));
Object ret = Subject.doAs(testSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.getAddressCount();
} catch (Exception e1) {
e1.printStackTrace();
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof SecurityException);
assertTrue(((Exception)ret).getMessage().contains("EDIT"));
// with updaters role we can access a specific method
testSubject.getPrincipals().add(new RolePrincipal("updaters"));
ret = Subject.doAs(testSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return serverControl.isSecurityEnabled();
} catch (Exception e1) {
e1.printStackTrace();
return e1;
}
});
assertNotNull(ret);
assertTrue(ret instanceof Boolean);
}
@Test
public void testQueryWithStar() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true);
Set<Role> roles = new HashSet<>();
roles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("mops.mbeanserver.queryNames", roles);
server.start();
Hashtable<String, String> attrs = new Hashtable<>();
attrs.put("broker", "bb");
attrs.put("type", "security");
attrs.put("area", "jmx");
attrs.put("name", "*");
final ObjectName queryName = new ObjectName("*", attrs);
Subject viewSubject = new Subject();
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
Object result = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return proxy.queryNames(queryName, null);
} catch (Exception e1) {
return e1;
}
});
assertNotNull(result);
assertTrue(result instanceof Set);
}
@Test
public void testQueryAllFiltered() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true);
Set<Role> viewerRole = new HashSet<>();
viewerRole.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
Set<Role> condoleRole = new HashSet<>();
condoleRole.add(new Role("mbeanServer", false, false, false, false, false, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("mops.mbeanserver.#", condoleRole);
server.getConfiguration().putSecurityRoles("mops.address.activemq.notifications", viewerRole);
server.start();
Subject viewSubject = new Subject();
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("mbeanServer"));
Object result = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return proxy.queryNames(null, null);
} catch (Exception e1) {
return e1;
}
});
assertNotNull(result);
assertEquals(1, ((Set) result).size());
// give view role
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
result = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Object>) () -> {
try {
return proxy.queryNames(null, null);
} catch (Exception e1) {
return e1;
}
});
assertNotNull(result);
assertEquals(2, ((Set) result).size());
// and they are there, we just don't see them
assertEquals(5, proxy.getMBeanCount().intValue());
}
@Test
public void testCanInvoke() throws Exception {
MBeanServer proxy = underTest.newMBeanServer("d", mbeanServer, mBeanServerDelegate);
final ActiveMQServer server = createServer(false);
server.setMBeanServer(proxy);
server.getConfiguration().setJMXManagementEnabled(true).setSecurityEnabled(true);
Set<Role> roles = new HashSet<>();
roles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("mops.java.#", roles);
server.start();
final HawtioSecurityControl securityControl = JMX.newMBeanProxy(
proxy, ObjectNameBuilder.DEFAULT.getSecurityObjectName(), HawtioSecurityControl.class, false);
ObjectName runtimeName = new ObjectName("java.lang", "type", "Runtime");
final RuntimeMXBean runtime = JMX.newMBeanProxy(
proxy, runtimeName, RuntimeMXBean.class, false);
Subject viewSubject = new Subject();
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
Object result = Subject.doAs(viewSubject, (PrivilegedAction<Object>) () -> {
try {
return securityControl.canInvoke(runtimeName.toString());
} catch (Exception e1) {
return e1.getCause();
}
});
assertNotNull(result);
assertFalse("in the absence of an operation to check, update required", (Boolean) result);
result = Subject.doAs(viewSubject, (PrivilegedAction<Object>) () -> {
try {
return securityControl.canInvoke(runtimeName.toString(), "getVmName");
} catch (Exception e1) {
return e1.getCause();
}
});
assertNotNull(result);
assertTrue((Boolean) result);
result = Subject.doAs(viewSubject, (PrivilegedAction<Object>) () -> {
try {
return securityControl.canInvoke(runtimeName.toString(), "getVmName", new String[]{"args", "are", "ignored"});
} catch (Exception e1) {
return e1.getCause();
}
});
assertNotNull(result);
assertTrue((Boolean) result);
Map<String, List<String>> bulkQuery = new HashMap<>();
bulkQuery.put(runtimeName.toString(), List.of("getVmName()", "getVersion()"));
result = Subject.doAs(viewSubject, (PrivilegedAction<Object>) () -> {
try {
return securityControl.canInvoke(bulkQuery);
} catch (Exception e1) {
return e1.getCause();
}
});
assertNotNull(result);
assertEquals(2, ((TabularData)result).size());
CompositeData cd = ((TabularData)result).get(new Object[]{runtimeName.toString(), "getVmName()"});
assertEquals(runtimeName.toString(), cd.get("ObjectName"));
assertEquals("getVmName()", cd.get("Method"));
assertEquals(true, cd.get("CanInvoke"));
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.impl;
import org.apache.activemq.artemis.core.server.management.ArtemisRbacInvocationHandler;
import org.junit.Before;
import org.mockito.Mockito;
public class HawtioSecurityControlImplSecurityStoreRbacTest extends HawtioSecurityControlImplTest {
@Override @Before
public void initGuard() {
guard = Mockito.mock(ArtemisRbacInvocationHandler.class);
}
}

View File

@ -26,6 +26,8 @@ import java.util.Map;
import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.server.management.ArtemisMBeanServerGuard;
import org.apache.activemq.artemis.core.server.management.GuardInvocationHandler;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
@ -36,10 +38,16 @@ import static org.junit.Assert.fail;
public class HawtioSecurityControlImplTest {
protected GuardInvocationHandler guard;
@Before
public void initGuard() {
guard = Mockito.mock(ArtemisMBeanServerGuard.class);
}
@Test
public void testCanInvokeMBean() throws Exception {
String objectName = "foo.bar.testing:type=SomeMBean";
ArtemisMBeanServerGuard guard = Mockito.mock(ArtemisMBeanServerGuard.class);
StorageManager storageManager = Mockito.mock(StorageManager.class);
Mockito.when(guard.canInvoke(objectName, null)).thenReturn(true);
@ -50,7 +58,6 @@ public class HawtioSecurityControlImplTest {
@Test
public void testCanInvokeMBean2() throws Exception {
String objectName = "foo.bar.testing:type=SomeMBean";
ArtemisMBeanServerGuard guard = Mockito.mock(ArtemisMBeanServerGuard.class);
StorageManager storageManager = Mockito.mock(StorageManager.class);
Mockito.when(guard.canInvoke(objectName, null)).thenReturn(false);
@ -61,7 +68,6 @@ public class HawtioSecurityControlImplTest {
@Test(expected = Exception.class)
public void testCanInvokeMBeanThrowsException() throws Exception {
String objectName = "foo.bar.testing:type=SomeMBean";
ArtemisMBeanServerGuard guard = Mockito.mock(ArtemisMBeanServerGuard.class);
StorageManager storageManager = Mockito.mock(StorageManager.class);
Mockito.when(guard.canInvoke(objectName, null)).thenThrow(new Exception());
@ -79,7 +85,6 @@ public class HawtioSecurityControlImplTest {
@Test
public void testCanInvokeMethod() throws Exception {
String objectName = "foo.bar.testing:type=SomeMBean";
ArtemisMBeanServerGuard guard = Mockito.mock(ArtemisMBeanServerGuard.class);
StorageManager storageManager = Mockito.mock(StorageManager.class);
Mockito.when(guard.canInvoke(objectName, "testMethod")).thenReturn(true);
Mockito.when(guard.canInvoke(objectName, "otherMethod")).thenReturn(false);
@ -93,7 +98,6 @@ public class HawtioSecurityControlImplTest {
@Test(expected = Exception.class)
public void testCanInvokeMethodException() throws Exception {
String objectName = "foo.bar.testing:type=SomeMBean";
ArtemisMBeanServerGuard guard = Mockito.mock(ArtemisMBeanServerGuard.class);
StorageManager storageManager = Mockito.mock(StorageManager.class);
Mockito.when(guard.canInvoke(objectName, "testMethod")).thenThrow(new Exception());
@ -110,7 +114,6 @@ public class HawtioSecurityControlImplTest {
@Test
public void testCanInvokeBulk() throws Exception {
ArtemisMBeanServerGuard guard = Mockito.mock(ArtemisMBeanServerGuard.class);
StorageManager storageManager = Mockito.mock(StorageManager.class);
String objectName = "foo.bar.testing:type=SomeMBean";
Mockito.when(guard.canInvoke(objectName, "testMethod")).thenReturn(true);
@ -153,7 +156,6 @@ public class HawtioSecurityControlImplTest {
@Test
public void testCanInvokeBulkWithDuplicateMethods() throws Exception {
ArtemisMBeanServerGuard guard = Mockito.mock(ArtemisMBeanServerGuard.class);
StorageManager storageManager = Mockito.mock(StorageManager.class);
String objectName = "foo.bar.testing:type=SomeMBean";
Mockito.when(guard.canInvoke(objectName, "duplicateMethod1")).thenReturn(true);

View File

@ -0,0 +1,217 @@
/*
* 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.impl;
import javax.management.MBeanServer;
import org.apache.activemq.artemis.api.core.RoutingType;
import org.apache.activemq.artemis.api.core.SimpleString;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.management.ResourceNames;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.impl.FileConfiguration;
import org.apache.activemq.artemis.core.paging.PagingManager;
import org.apache.activemq.artemis.core.paging.PagingStore;
import org.apache.activemq.artemis.core.persistence.StorageManager;
import org.apache.activemq.artemis.core.postoffice.PostOffice;
import org.apache.activemq.artemis.core.security.CheckType;
import org.apache.activemq.artemis.core.security.SecurityAuth;
import org.apache.activemq.artemis.core.security.SecurityStore;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.Queue;
import org.apache.activemq.artemis.core.server.impl.AddressInfo;
import org.apache.activemq.artemis.spi.core.remoting.Acceptor;
import org.apache.activemq.artemis.utils.ExecutorFactory;
import org.apache.activemq.artemis.utils.actors.ArtemisExecutor;
import org.junit.Test;
import org.mockito.Mockito;
public class ManagementServiceImplTest {
MBeanServer mBeanServer = Mockito.mock(MBeanServer.class);
SecurityStore securityStore = Mockito.mock(SecurityStore.class);
ActiveMQServer messagingServer = Mockito.mock(ActiveMQServer.class);
ExecutorFactory executorFactory = Mockito.mock(ExecutorFactory.class);
ArtemisExecutor artemisExecutor = Mockito.mock(ArtemisExecutor.class);
PostOffice postOffice = Mockito.mock(PostOffice.class);
SecurityAuth auth = Mockito.mock(SecurityAuth.class);
PagingManager pagingManager = Mockito.mock(PagingManager.class);
private PagingStore pageStore = Mockito.mock(PagingStore.class);
@Test
public void testGetAttributeSecurityCheck() throws Exception {
Configuration configuration = new FileConfiguration();
configuration.setManagementMessageRbac(true);
configuration.setManagementRbacPrefix("mm");
configuration.setViewPermissionMethodMatchPattern("^get.*$"); // no match for isPaging
ManagementServiceImpl managementService = new ManagementServiceImpl(mBeanServer, configuration);
Mockito.when(executorFactory.getExecutor()).thenReturn(artemisExecutor);
Mockito.when(messagingServer.getExecutorFactory()).thenReturn(executorFactory);
Mockito.when(messagingServer.getManagementService()).thenReturn(managementService);
Mockito.when(postOffice.isStarted()).thenReturn(true);
Mockito.when(messagingServer.getPostOffice()).thenReturn(postOffice);
Mockito.when(pagingManager.getPageStore(Mockito.any(SimpleString.class))).thenReturn(pageStore);
Mockito.when(pageStore.isPaging()).thenReturn(true);
managementService.registerServer(null, securityStore, null, configuration, null, null, null, null, messagingServer, null, null, pagingManager, false);
// acceptor
Mockito.clearInvocations(securityStore);
Acceptor acceptor = Mockito.mock(Acceptor.class);
TransportConfiguration transportConfig = Mockito.mock(TransportConfiguration.class);
Mockito.when(transportConfig.getName()).thenReturn("a1");
managementService.registerAcceptor(acceptor, transportConfig);
managementService.getAttribute(ResourceNames.ACCEPTOR + transportConfig.getName(), "name", auth);
SimpleString expected = SimpleString.toSimpleString("mm.acceptor.a1.getName");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.VIEW), Mockito.any(SecurityAuth.class));
// address
final String addressName = "addr1";
Mockito.clearInvocations(securityStore);
managementService.registerAddress(new AddressInfo(addressName));
managementService.getAttribute(ResourceNames.ADDRESS + addressName, "address", auth);
expected = SimpleString.toSimpleString("mm.address." + addressName + ".getAddress");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.VIEW), Mockito.any(SecurityAuth.class));
// isX needs UPDATE with view regexp above
Mockito.clearInvocations(securityStore);
managementService.getAttribute(ResourceNames.ADDRESS + addressName, "paging", auth);
expected = SimpleString.toSimpleString("mm.address." + addressName + ".isPaging");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.EDIT), Mockito.any(SecurityAuth.class));
// queue
final SimpleString queueName = SimpleString.toSimpleString("queueName");
Mockito.clearInvocations(securityStore);
Queue queue = Mockito.mock(Queue.class);
Mockito.when(queue.getName()).thenReturn(queueName);
Mockito.when(queue.getRoutingType()).thenReturn(RoutingType.ANYCAST);
StorageManager storageManager = Mockito.mock(StorageManager.class);
managementService.registerQueue(queue, new AddressInfo(queueName), storageManager);
managementService.getAttribute(ResourceNames.QUEUE + queueName, "ringSize", auth);
expected = SimpleString.toSimpleString("mm.queue." + queueName + ".getRingSize");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.VIEW), Mockito.any(SecurityAuth.class));
Mockito.clearInvocations(securityStore);
managementService.getAttribute(ResourceNames.QUEUE + queueName, "ID", auth);
expected = SimpleString.toSimpleString("mm.queue." + queueName + ".getID");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.VIEW), Mockito.any(SecurityAuth.class));
}
@Test
public void testInvokeSecurityCheck() throws Exception {
Configuration configuration = new FileConfiguration();
configuration.setManagementMessageRbac(true);
configuration.setManagementRbacPrefix("$mm");
ManagementServiceImpl managementService = new ManagementServiceImpl(mBeanServer, configuration);
Mockito.when(executorFactory.getExecutor()).thenReturn(artemisExecutor);
Mockito.when(messagingServer.getExecutorFactory()).thenReturn(executorFactory);
Mockito.when(messagingServer.getManagementService()).thenReturn(managementService);
Mockito.when(postOffice.isStarted()).thenReturn(true);
Mockito.when(messagingServer.getPostOffice()).thenReturn(postOffice);
managementService.registerServer(null, securityStore, null, configuration, null, null, null, null, messagingServer, null, null, null, false);
// acceptor
Mockito.clearInvocations(securityStore);
Acceptor acceptor = Mockito.mock(Acceptor.class);
TransportConfiguration transportConfig = Mockito.mock(TransportConfiguration.class);
Mockito.when(transportConfig.getName()).thenReturn("a1");
managementService.registerAcceptor(acceptor, transportConfig);
managementService.invokeOperation(ResourceNames.ACCEPTOR + transportConfig.getName(), "getName", new Object[]{}, auth);
SimpleString expected = SimpleString.toSimpleString("$mm.acceptor.a1.getName");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.VIEW), Mockito.any(SecurityAuth.class));
// address
final String addressName = "addr1";
Mockito.clearInvocations(securityStore);
managementService.registerAddress(new AddressInfo(addressName));
managementService.invokeOperation(ResourceNames.ADDRESS + addressName, "getAddress", new Object[]{}, auth);
expected = SimpleString.toSimpleString("$mm.address." + addressName + ".getAddress");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.VIEW), Mockito.any(SecurityAuth.class));
// queue
final SimpleString queueName = SimpleString.toSimpleString("queueName");
Mockito.clearInvocations(securityStore);
Queue queue = Mockito.mock(Queue.class);
Mockito.when(queue.getName()).thenReturn(queueName);
Mockito.when(queue.getRoutingType()).thenReturn(RoutingType.ANYCAST);
StorageManager storageManager = Mockito.mock(StorageManager.class);
managementService.registerQueue(queue, new AddressInfo(queueName), storageManager);
managementService.invokeOperation(ResourceNames.QUEUE + queueName, "getRingSize", new Object[]{}, auth);
expected = SimpleString.toSimpleString("$mm.queue." + queueName + ".getRingSize");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.VIEW), Mockito.any(SecurityAuth.class));
// update permission required on pause operation
Mockito.clearInvocations(securityStore);
managementService.invokeOperation(ResourceNames.QUEUE + queueName, "pause", new Object[]{}, auth);
expected = SimpleString.toSimpleString("$mm.queue." + queueName + ".pause");
Mockito.verify(securityStore).check(Mockito.eq(expected), Mockito.eq(CheckType.EDIT), Mockito.any(SecurityAuth.class));
}
@Test
public void testGetAttributeNoSecurityCheck() throws Exception {
Configuration configuration = new FileConfiguration();
ManagementServiceImpl managementService = new ManagementServiceImpl(mBeanServer, configuration);
Mockito.when(executorFactory.getExecutor()).thenReturn(artemisExecutor);
Mockito.when(messagingServer.getExecutorFactory()).thenReturn(executorFactory);
Mockito.when(messagingServer.getManagementService()).thenReturn(managementService);
Mockito.when(postOffice.isStarted()).thenReturn(true);
Mockito.when(messagingServer.getPostOffice()).thenReturn(postOffice);
managementService.registerServer(null, securityStore, null, configuration, null, null, null, null, messagingServer, null, null, null, false);
// acceptor
Mockito.clearInvocations(securityStore);
Acceptor acceptor = Mockito.mock(Acceptor.class);
TransportConfiguration transportConfig = Mockito.mock(TransportConfiguration.class);
Mockito.when(transportConfig.getName()).thenReturn("a1");
managementService.registerAcceptor(acceptor, transportConfig);
managementService.getAttribute(ResourceNames.ACCEPTOR + transportConfig.getName(), "name", auth);
Mockito.verifyNoInteractions(securityStore);
}
}

View File

@ -181,13 +181,13 @@ public class RepositoryTest extends ServerTestBase {
public void testSingletwo() {
securityRepository.addMatch("queues.another.aq.*", new HashSet<Role>());
HashSet<Role> roles = new HashSet<>(2);
roles.add(new Role("test1", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("test2", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("test1", true, true, true, true, true, true, true, true, true, true, false, false));
roles.add(new Role("test2", true, true, true, true, true, true, true, true, true, true, false, false));
securityRepository.addMatch("queues.aq", roles);
HashSet<Role> roles2 = new HashSet<>(2);
roles2.add(new Role("test1", true, true, true, true, true, true, true, true, true, true));
roles2.add(new Role("test2", true, true, true, true, true, true, true, true, true, true));
roles2.add(new Role("test3", true, true, true, true, true, true, true, true, true, true));
roles2.add(new Role("test1", true, true, true, true, true, true, true, true, true, true, false, false));
roles2.add(new Role("test2", true, true, true, true, true, true, true, true, true, true, false, false));
roles2.add(new Role("test3", true, true, true, true, true, true, true, true, true, true, false, false));
securityRepository.addMatch("queues.another.andanother", roles2);
HashSet<Role> hashSet = securityRepository.getMatch("queues.another.andanother");
@ -198,8 +198,8 @@ public class RepositoryTest extends ServerTestBase {
public void testWithoutWildcard() {
securityRepository.addMatch("queues.1.*", new HashSet<Role>());
HashSet<Role> roles = new HashSet<>(2);
roles.add(new Role("test1", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("test2", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("test1", true, true, true, true, true, true, true, true, true, true, false, false));
roles.add(new Role("test2", true, true, true, true, true, true, true, true, true, true, false, false));
securityRepository.addMatch("queues.2.aq", roles);
HashSet<Role> hashSet = securityRepository.getMatch("queues.2.aq");
Assert.assertEquals(hashSet.size(), 2);

View File

@ -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.spi.core.security;
import javax.security.auth.Subject;
import java.security.Principal;
import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class ActiveMQJAASSecurityManagerTest {
final String user = "P";
class UserFromOtherDomainPrincipal implements Principal {
@Override
public String getName() {
return user;
}
}
@Test
public void getUserFromSubjectWithExternalPrinciple() throws Exception {
ActiveMQJAASSecurityManager underTest = new ActiveMQJAASSecurityManager();
assertEquals(UserPrincipal.class.getName(), underTest.getUserPrincipalClass());
assertEquals(RolePrincipal.class.getName(), underTest.getRolePrincipalClass());
Subject subject = new Subject();
subject.getPrincipals().add(new ActiveMQJAASSecurityManagerTest.UserFromOtherDomainPrincipal());
assertNull(underTest.getUserFromSubject(subject));
underTest.setUserPrincipalClass(ActiveMQJAASSecurityManagerTest.UserFromOtherDomainPrincipal.class.getName());
assertEquals(user, underTest.getUserFromSubject(subject));
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.security.Principal;
import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
import org.junit.Assert;
import org.junit.Test;
public class SecurityManagerUtilTest {
@Test
public void getUserFromSubject() throws Exception {
Assert.assertNull(SecurityManagerUtil.getUserFromSubject(null, null));
Subject subject = new Subject();
Assert.assertNull(SecurityManagerUtil.getUserFromSubject(subject, UserPrincipal.class));
subject.getPrincipals().add(new RolePrincipal("r"));
Assert.assertNull(SecurityManagerUtil.getUserFromSubject(subject, UserPrincipal.class));
subject.getPrincipals().add(new UserPrincipal("u"));
Assert.assertEquals("u", SecurityManagerUtil.getUserFromSubject(subject, UserPrincipal.class));
}
class UserFromOtherDomainPrincipal implements Principal {
@Override
public String getName() {
return "P";
}
}
@Test
public void getUserFromForeignPrincipalInSubject() throws Exception {
Subject subject = new Subject();
subject.getPrincipals().add(new UserFromOtherDomainPrincipal());
Assert.assertNull(SecurityManagerUtil.getUserFromSubject(subject, UserPrincipal.class));
}
}

View File

@ -0,0 +1,55 @@
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.security.PrivilegedExceptionAction;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class SubjectDotDoAsRule implements TestRule {
final Subject subject;
public SubjectDotDoAsRule(Subject subject) {
this.subject = subject;
}
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
Exception e = Subject.doAs(subject, (PrivilegedExceptionAction<Exception>) () -> {
try {
base.evaluate();
} catch (Throwable e1) {
return new Exception(e1);
}
return null;
});
if (e != null) {
throw e;
}
}
};
}
}

View File

@ -591,6 +591,19 @@ In most cases this should be set to '1'.
| xref:wildcard-syntax.adoc#wildcard-syntax[wildcard-addresses]
| parameters to configure wildcard address matching format.
| n/a
| [[view-permission-method-match-pattern]] view-permission-method-match-pattern
| parameter to configure the regular expression pattern to match xref:management.adoc#fine-grained-rbac-on-management-messages[management] or xref:management.adoc#jmx-authorization-in-broker-xml[JMX] operations that require the 'view' permission
in your security-settings.
| ``^(get\|is\|count\|list\|browse\|query).*$``
| [[management-message-rbac]] management-message-rbac
| parameter to enable security-settings RBAC on xref:management.adoc#fine-grained-rbac-on-management-messages[management messages] sent to the management address.
| false
| [[management-rbac-prefix]] management-rbac-prefix
| parameter to configure the prefix for security-settings match addresses to control RBAC on xref:management.adoc#jmx-authorization-in-broker-xml[JMX MBean operations] and optionally on xref:management.adoc#fine-grained-rbac-on-management-messages[management messages]
| mops (shorthand for management operations)
|===
== address-setting type

View File

@ -75,6 +75,7 @@ diverts) can be created or destroyed using the management operations `createBrid
+
Diverts can be updated using the management operation `updateDivert()`.
[#force_failover]
* It is possible to stop the server and force failover to occur with any currently attached clients.
+
To do this use the `forceFailover()` operation.
@ -189,7 +190,7 @@ The acceptors parameters can be retrieved using the `AcceptorControl` attributes
* Diverts
+
They can be started or stopped using the `start()` or `stop()` method on the `DivertControl` interface.
Diverts parameters can be retrieved using the `DivertControl` attributes (see xref:diverts.adoc#diverting-and-splitting-message-flows[Diverting and Splitting Message Flows)])
Diverts parameters can be retrieved using the `DivertControl` attributes (see xref:diverts.adoc#diverting-and-splitting-message-flows[Diverting and Splitting Message Flows])
* Bridges
+
@ -250,11 +251,14 @@ It can be disabled by setting `jmx-management-enabled` to `false` in `broker.xml
==== Role Based Authorisation for JMX
Although by default Artemis uses the Java Virtual Machine's `Platform MBeanServer` this is guarded using role based authorisation that leverages the broker's JAAS plugin support.
This is configured via the `authorisation` element in the `management.xml` configuration file and can be used to restrict access to attributes and methods on MBeans.
Artemis uses the Java Virtual Machine's `Platform MBeanServer` by default. This is guarded using role based authorisation that leverages the broker's JAAS plugin support.
The RBAC used to restrict access to Mbeans and their operations can be configured in `one` of two ways. Via security-settings in broker.xml, described in xref:management.adoc#jmx-authorization-in-broker-xml[JMX authorization in broker.xml], or via the `authorization` element in the `management.xml` that is described below.
===== JMX authorisation in management.xml
There are 3 elements within the `authorisation` element, `allowlist`, `default-access` and `role-access`.
Lets discuss each in turn.
Let's discuss each in turn.
Allowlist contains a list of MBeans that will bypass the authorisation, this is typically used for any MBeans that are needed by the console to run etc.
The default configuration is:
@ -301,7 +305,7 @@ The default configuration looks like:
This contains 1 match and will be applied to any MBean that has the domain `org.apache.activemq.artemis`.
Any access to any MBeans that have this domain are controlled by the `access` elements which contain a method and a set of roles.
The method being invoked will be used to pick the closest matching method and the roles for this will be applied for access.
For instance if you try the invoke a method called `listMessages` on an MBean with the `org.apache.activemq.artemis` domain then this would match the `access` with the method of `list*`.
For instance if you try to invoke a method called `listMessages` on an MBean with the `org.apache.activemq.artemis` domain then this would match the `access` with the method of `list*`.
You could also explicitly configure this by using the full method name, like so:
[,xml]
@ -357,11 +361,66 @@ You can also use wildcards for the MBean properties so the following would also
In case of multiple matches, the exact matches have higher priority than the wildcard matches and the longer wildcard matches have higher priority than the shorter wildcard matches.
Access to JMX MBean attributes are converted to method calls so these are controlled via the `set*`, `get*` and `is*`.
The `*` access is the catch all for everything other method that isn't specifically matched.
The `*` access is the catch-all for everything other method that isn't specifically matched.
The `default-access` element is basically the catch all for every method call that isn't handled via the `role-access` configuration.
The `default-access` element is basically the catch-all for every method call that isn't handled via the `role-access` configuration.
This has the same semantics as a `match` element.
==== JMX authorization in broker.xml
The existing xref:security.adoc#role-based-security-for-addresses[security-settings] in broker.xml can be used for JMX RBAC.
Using the `view` and `edit` permissions on matches in security-settings provides an alternative to the authorization section in management.xml.
Using a single security model based on addresses, with reloadable configuration, simplifies operation.
An xref:management.adoc#artemis_rbac_mbean_server_guard[MBeanServer interceptor] that delegates to the broker security manager must be configured with a JVM system property that allows it to be added to all MBeanServers in the JVM.
This is configured via a system property as follows:
[,sh]
----
java -Djavax.management.builder.initial=org.apache.activemq.artemis.core.server.management.ArtemisRbacMBeanServerBuilder
----
IMPORTANT: When this property is provided, the authorization section of management.xml should be omitted as that depends on an alternative MBeanServer interceptor.
The security-settings match addresses used for JMX RBAC use the `mops.` (shorthand for management operations) xref:configuration-index.adoc#management-rbac-prefix[prefix]. This allows independent RBAC between messaging operations and management operations.
The MBeanServer guard maps JMX MBean ObjectNames to a hierarchical address of the general form:
mops<.jmx domain><.type><.component><.name>[.operation]
NOTE: for the broker domain, the domain is omitted.
For example, to give the `admin` role `view` and `edit` permissions on all MBeans, use the following security-setting:
[,xml]
----
<security-setting match="mops.#">
<permission type="view" roles="admin"/>
<permission type="edit" roles="admin"/>
</security-setting>
----
To grant the `managerRole` role `view` permission to just the `activemq.management` address, target the `address` component with name `activemq.management` and with `.*` to include all operations.
[,xml]
----
<security-setting match="mops.address.activemq.management.*">
<permission type="view" roles="managerRole"/>
</security-setting>
----
To ensure no user has permission to xref:management.adoc#force_failover[force a failover] using the broker (server control) MBean, use the following that defines the empty roles set for a particular mutating operation on the `broker` component:
[,xml]
----
<security-setting match="mops.broker.forceFailover">
<permission type="edit" roles=""/>
</security-setting>
----
==== Local JMX Access with JConsole
Due to the authorisation which is enabled by default Apache ActiveMQ Artemis can _not_ be managed locally using JConsole when connecting as a _local process_.
@ -370,7 +429,7 @@ In order to use JConsole the user will either have to disable authorisation by c
==== Remote JMX Access
By default remote JMX access to Artemis is disabled for security reasons.
By default, remote JMX access to Artemis is disabled for security reasons.
Artemis has a JMX agent which allows access to JMX MBeans remotely.
This is configured via the `connector` element in the `management.xml` configuration file.
@ -505,14 +564,24 @@ Such a `curl` command would give you back something like the following (after fo
=== JMX and the Web Console
The web console that ships with Artemis uses Jolokia under the covers which in turn uses JMX.
This will use the authentication configuration in the `management.xml` file as described in the previous section.
This will use the authentication configuration as described in the xref:management.adoc#role-based-authorisation-for-jmx[Role Based Authorisation for JMX section].
This means that when MBeans are accessed via the console the credentials used to log into the console and the roles associated with them.
By default access to the console is only allow via users with the `amq` role.
By default, access to the console is only allow via users with the `amq` role.
This is configured in the `artemis.profile` via the system property `-Dhawtio.role=amq`.
You can configure multiple roles by changing this to `-Dhawtio.roles=amq,view,update`.
If a user doesn't have the correct role to invoke a specific operation then this will display an authorisation exception in the console.
[#artemis_rbac_mbean_server_guard]
==== ArtemisRbacMBeanServerBuilder and ArtemisRbacInvocationHandler
The ArtemisRbacMBeanServerBuilder class, when configured as value for the system property `javax.management.builder.initial` will cause the ArtemisRbacInvocationHandler to be installed on every JMX MBeanServer in the JVM.
The ArtemisRbacInvocationHandler intercepts all operations on the MBeanServer and chooses to guard a subsection of those operations.
For guarded operations the `view` or `edit` permissions are required to make an invocation. If the current authenticated subject does not have the required roles to grant those permissions, a security exception is thrown.
For query operations on the MBeanServer, the results of the query are limited to entries that have the required `view` permission.
== Using Management Message API
The management message API in ActiveMQ Artemis is accessed by sending Core Client messages to a special address, the _management address_.
@ -585,7 +654,43 @@ This is also configured in broker.xml:
</security-setting>
----
=== Example
=== Fine grained RBAC on management messages
There is optional RBAC on the content of management messages sent to the management address.
RBAC is enabled through configuration by setting the attribute xref:configuration-index.adoc#management-message-rbac[management-message-rbac] to `true`.
NOTE: The `manage` permission is required to execute management operations via messages. The `view` and `edit` permissions must be used in conjunction with the `manage` permission.
When enabled, more fine-grained permissions on the content of management messages sent to the management address can be configured through the security-settings.
The security-settings match addresses used for RBAC follow the general hierarchical form of: xref:configuration-index.adoc#management-rbac-prefix[management-rbac-prefix], component type, component name, operation. Where the values are extracted from the management message headers.
<management-rbac-prefix>.<resource type>.<resource name>.<operation>
xref:configuration-index.adoc#view-permission-method-match-pattern[Immutable operations and attribute access] will require the `view` permission, all other operations will require the `edit` permission.
In the following example the `dataImport` role can only access the id attribute of queues, which is the only management operation that is required by the xref:using-cli.adoc#command-line-interface[data import] command line tool.
[,xml]
----
<security-setting match="mops.queue.*.getID">
<permission type="view" roles="dataImport" />
<permission type="manage" roles="dataImport" />
</security-setting>
----
If you want the `admin` role to have full access, use a wildcard after the management-rbac-prefix and grant both the `view` and `edit` permissions:
[,xml]
----
<security-setting match="mops.#">
<permission type="view" roles="admin" />
<permission type="update" roles="admin" />
<permission type="manage" roles="admin" />
</security-setting>
----
=== Management Example
See the xref:examples.adoc#management[Management Example] which shows how to use JMS messages to manage the Apache ActiveMQ Artemis server.

View File

@ -68,6 +68,14 @@ This permission allows the user to browse a queue bound to the matching address.
manage::
This permission allows the user to invoke management operations by sending management messages to the management address.
The following two permissions pertain to operations on the xref:management.adoc#management[management apis] of the broker. They split management operations into two sets, read only for `view`, and `edit` for mutating operations. The split is controlled by a regular expression. Methods that match will require the `view` permission, all others require `edit`. The regular expression can be modified through the configuration attribute xref:configuration-index.adoc#view-permission-method-match-pattern[`view-permission-method-match-pattern`]. These permissions are applicable to the xref:management.adoc#fine-grained-rbac-on-management-messages[management address] and to xref:management.adoc#jmx-authorization-in-broker-xml[MBean access]. They are granted to match addresses prefixed with the xref:configuration-index.adoc#management-rbac-prefix[management prefix].
view::
This permission allows access to a read-only subset of management operations.
update::
This permission allows access to the mutating management operations, any operation not in the `view` set.
For each permission, a list of roles who are granted that permission is specified.
If the user has any of those roles, he/she will be granted that permission for that set of addresses.
@ -153,9 +161,18 @@ You can do this using the fully qualified queue name (i.e. FQQN) in the `match`
</security-setting>
----
NOTE: Wildcard matching doesn't work in conjuction with FQQN.
NOTE: Wildcard matching doesn't work in conjunction with FQQN.
The explicit goal of using FQQN here is to be _exact_.
=== Applying `view` and `edit` permissions to the management api
The `view` and `edit` permissions are optionally applied to the management apis of the broker.
For RBAC on JMX MBean access they can replace the authorization section in management.xml as described at xref:management.adoc#jmx-authorization-in-broker-xml[JMX authorization in broker.xml]
For RBAC on management resources accessed via messages sent to the management address, the additional permissions are enabled by configuring xref:configuration-index.adoc#management-message-rbac[`management-message-rbac`] as described at xref:management.adoc#fine-grained-rbac-on-management-messages[Fine grained RBAC on management messages]
The split between operations that require the `view` and `edit` permissions can be controlled via xref:configuration-index.adoc#view-permission-method-match-pattern[view-permission-method-match-pattern]
== Security Setting Plugin
Aside from configuring sets of permissions via XML these permissions can alternatively be configured via a plugin which implements `org.apache.activemq.artemis.core.server.SecuritySettingPlugin` e.g.:

View File

@ -133,7 +133,7 @@ public class JMSSaslExternalLDAPTest extends AbstractLdapTestUnit {
// role mapping via CertLogin - TextFileCertificateLoginModule
final String roleName = "widgets";
Role role = new Role(roleName, true, true, true, true, true, true, true, true, true, true);
Role role = new Role(roleName, true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch("TEST", roles);

View File

@ -400,7 +400,7 @@ public class SaslKrb5LDAPSecurityTest extends AbstractLdapTestUnit {
createArtemisServer(jaasConfigScope);
Set<Role> roles = new HashSet<>();
roles.add(new Role(artemisRoleName, true, true, true, true, true, true, true, true, true, true));
roles.add(new Role(artemisRoleName, true, true, true, true, true, true, true, true, true, true, false, false));
server.getConfiguration().putSecurityRoles(QUEUE_NAME, roles);
server.start();

View File

@ -0,0 +1,139 @@
/*
* 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.isolated.security;
import javax.management.JMX;
import javax.security.auth.Subject;
import java.lang.management.ManagementFactory;
import java.security.PrivilegedExceptionAction;
import java.util.HashSet;
import java.util.Set;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl;
import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMAcceptorFactory;
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.management.ArtemisRbacMBeanServerBuilder;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal;
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class JmxSecurityMultipleSubjectTest {
public static Subject viewSubject = new Subject();
public static Subject updateSubject = new Subject();
static {
System.setProperty("javax.management.builder.initial", ArtemisRbacMBeanServerBuilder.class.getCanonicalName());
viewSubject.getPrincipals().add(new UserPrincipal("v"));
viewSubject.getPrincipals().add(new RolePrincipal("viewers"));
updateSubject.getPrincipals().add(new UserPrincipal("u"));
updateSubject.getPrincipals().add(new RolePrincipal("updaters"));
}
ActiveMQServer server;
@Before
public void setUp() throws Exception {
ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("PropertiesLogin");
Configuration configuration = new ConfigurationImpl().addAcceptorConfiguration(new TransportConfiguration(InVMAcceptorFactory.class.getCanonicalName()));
configuration.setManagementRbacPrefix("jmx");
server = ActiveMQServers.newActiveMQServer(configuration, ManagementFactory.getPlatformMBeanServer(), securityManager, false);
}
@After
public void tearDown() throws Exception {
server.stop();
}
@Test
public void testJmxAuthBrokerNotCached() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false));
roles.add(new Role("updaters", false, false, false, false, false, false, false, false, false, false, true, true));
server.getConfiguration().putSecurityRoles("jmx.broker.addConnector", roles);
server.getConfiguration().putSecurityRoles("jmx.broker.getActivationSequence", roles);
server.getConfiguration().putSecurityRoles("jmx.broker.forceFailover", new HashSet<>());
server.start();
ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), server.getConfiguration().getName(), true);
final ActiveMQServerControl activeMQServerControl = JMX.newMBeanProxy(ManagementFactory.getPlatformMBeanServer(), objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
try {
activeMQServerControl.getActivationSequence();
fail("not logged in");
} catch (Exception expectedOnNotLoggedIn) {
assertTrue(expectedOnNotLoggedIn.getMessage().contains("management"));
}
// requiring update
Exception e = Subject.doAs(updateSubject, (PrivilegedExceptionAction<Exception>) () -> {
try {
// update first
activeMQServerControl.addConnector("c", "tcp://localhost:89");
// also exercise view
activeMQServerControl.getActivationSequence();
return null;
} catch (Exception e1) {
e1.printStackTrace();
return e1;
}
});
assertNull(e);
e = Subject.doAs(viewSubject, (PrivilegedExceptionAction<Exception>) () -> {
try {
activeMQServerControl.addConnector("d", "tcp://localhost:89");
return null;
} catch (Exception e1) {
return e1;
}
});
assertNotNull("view permission is not sufficient", e);
e = Subject.doAs(updateSubject, (PrivilegedExceptionAction<Exception>) () -> {
try {
activeMQServerControl.forceFailover();
return null;
} catch (Exception e1) {
return e1;
}
});
assertNotNull("no permission is sufficient", e);
}
}

View File

@ -0,0 +1,355 @@
/*
* 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.isolated.security;
import javax.management.JMX;
import javax.management.ObjectName;
import javax.security.auth.Subject;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.security.PrivilegedExceptionAction;
import java.util.HashSet;
import java.util.Set;
import jdk.management.jfr.FlightRecorderMXBean;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
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.TransportConfiguration;
import org.apache.activemq.artemis.api.core.management.AcceptorControl;
import org.apache.activemq.artemis.api.core.management.ActiveMQServerControl;
import org.apache.activemq.artemis.api.core.management.AddressControl;
import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
import org.apache.activemq.artemis.api.core.management.QueueControl;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.config.CoreAddressConfiguration;
import org.apache.activemq.artemis.core.config.impl.ConfigurationImpl;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMAcceptorFactory;
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.core.server.management.ArtemisRbacMBeanServerBuilder;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
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.SubjectDotDoAsRule;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class JmxSecurityTest {
public static Subject subject = new Subject();
static {
// before any call to ManagementFactory.getPlatformMBeanServer()
System.setProperty("javax.management.builder.initial", ArtemisRbacMBeanServerBuilder.class.getCanonicalName());
subject.getPrincipals().add(new UserPrincipal("first"));
subject.getPrincipals().add(new RolePrincipal("programmers"));
}
@Rule
public SubjectDotDoAsRule doAs = new SubjectDotDoAsRule(subject);
ActiveMQServer server;
@Before
public void setUp() {
ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("PropertiesLogin");
Configuration configuration = new ConfigurationImpl().addAcceptorConfiguration(new TransportConfiguration(InVMAcceptorFactory.class.getCanonicalName()));
configuration.setManagementRbacPrefix("jmx");
server = ActiveMQServers.newActiveMQServer(configuration, ManagementFactory.getPlatformMBeanServer(), securityManager, false);
}
@After
public void tearDown() throws Exception {
server.stop();
}
@Test
public void testJmxAuthBroker() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, true, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("jmx.broker.getActivationSequence", roles);
server.start();
ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), server.getConfiguration().getName(), true);
ActiveMQServerControl activeMQServerControl = JMX.newMBeanProxy(ManagementFactory.getPlatformMBeanServer(), objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false);
activeMQServerControl.getActivationSequence();
}
@Test
public void testJxmAuthUpdateAddressNegative() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, true, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("activemq.notifications", roles);
server.start();
AddressControl addressControl = JMX.newMBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
ObjectNameBuilder.DEFAULT.getAddressObjectName(SimpleString.toSimpleString("activemq.notifications")), AddressControl.class, false);
try {
addressControl.sendMessage(null, 1, "hi", false, null, null);
fail("need Update permission");
} catch (Exception expected) {
assertTrue(expected.getMessage().contains("first"));
assertTrue(expected.getMessage().contains("EDIT"));
}
}
@Test
public void testJxmAuthUpdateAddress() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", true, false, false, false, true, false, false, false, false, false, true, true));
server.getConfiguration().putSecurityRoles("jmx.address.activemq.notifications.getUnRoutedMessageCount", roles);
server.getConfiguration().putSecurityRoles("jmx.address.activemq.notifications.sendMessage", roles);
server.getConfiguration().putSecurityRoles("activemq.notifications", roles); // the real address Send permission
server.getConfiguration().putSecurityRoles("jmx.address.activemq.notifications.pause", roles);
server.getConfiguration().putSecurityRoles("jmx.address.activemq.notifications.resume", roles);
server.getConfiguration().putSecurityRoles("jmx.address.activemq.notifications.isPaging", roles);
server.start();
AddressControl addressControl = JMX.newMBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
ObjectNameBuilder.DEFAULT.getAddressObjectName(SimpleString.toSimpleString("activemq.notifications")), AddressControl.class, false);
long unRoutedMessageCount = addressControl.getUnRoutedMessageCount();
assertEquals(0L, unRoutedMessageCount);
addressControl.sendMessage(null, 1, "hi", false, null, null);
long unRoutedMessageCountAfter = addressControl.getUnRoutedMessageCount();
assertEquals(3L, unRoutedMessageCountAfter);
assertFalse(addressControl.isPaging());
addressControl.pause();
addressControl.resume();
}
@Test
public void testJmxAuthQueue() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, true, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("jmx.queue.Q1.*", roles);
CoreAddressConfiguration address = new CoreAddressConfiguration();
address.setName("Q1").addQueueConfig(new QueueConfiguration("Q1").setRoutingType(RoutingType.ANYCAST));
server.getConfiguration().getAddressConfigurations().add(address);
server.start();
QueueControl queueControl = JMX.newMBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
ObjectNameBuilder.DEFAULT.getQueueObjectName(SimpleString.toSimpleString("Q1"), SimpleString.toSimpleString("Q1"), RoutingType.ANYCAST), QueueControl.class, false);
queueControl.getDurableMessageCount();
queueControl.browse();
queueControl.countMessages();
queueControl.listGroupsAsJSON();
try {
queueControl.sendMessage(null, 1, "hi", false, null, null);
fail("need Update permission");
} catch (Exception expected) {
assertTrue(expected.getMessage().contains("first"));
assertTrue(expected.getMessage().contains("EDIT"));
}
try {
queueControl.disable();
fail("need Update permission");
} catch (Exception expected) {
assertTrue(expected.getMessage().contains("first"));
assertTrue(expected.getMessage().contains("EDIT"));
}
try {
queueControl.enable();
fail("need Update permission");
} catch (Exception expected) {
assertTrue(expected.getMessage().contains("first"));
assertTrue(expected.getMessage().contains("EDIT"));
}
try {
queueControl.pause(true);
fail("need Update permission");
} catch (Exception expected) {
assertTrue(expected.getMessage().contains("first"));
assertTrue(expected.getMessage().contains("EDIT"));
}
try {
queueControl.flushExecutor();
fail("need Update permission");
} catch (Exception expected) {
assertTrue(expected.getMessage().contains("first"));
assertTrue(expected.getMessage().contains("EDIT"));
}
}
@Test
public void testJxmAuthAcceptor() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, true, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("jmx.acceptors.*.isStarted", roles);
server.getConfiguration().putSecurityRoles("jmx.acceptors.*.getParameters", roles);
server.start();
AcceptorControl control = JMX.newMBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
ObjectNameBuilder.DEFAULT.getAcceptorObjectName(server.getConfiguration().getAcceptorConfigurations().stream().findFirst().get().getName()), AcceptorControl.class, false);
control.isStarted();
control.getParameters();
try {
control.stop();
fail("need Update permission");
} catch (Exception expected) {
assertTrue(expected.getMessage().contains("first"));
assertTrue(expected.getMessage().contains("EDIT"));
}
}
@Test
public void testJxmAuthJvmRuntime() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, true, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("jmx.java.lang.Runtime.*", roles);
server.start();
ObjectName runtimeName = new ObjectName("java.lang", "type", "Runtime");
RuntimeMXBean runtime = JMX.newMBeanProxy(
ManagementFactory.getPlatformMBeanServer(), runtimeName, RuntimeMXBean.class, false);
runtime.getVmVersion();
}
@Test
public void testJxmAuthFlightRecorder() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, true, false, false, false, false, false, true, false));
server.getConfiguration().putSecurityRoles("jmx.jdk.management.#", roles);
server.start();
ObjectName runtimeName = new ObjectName("jdk.management.jfr", "type", "FlightRecorder");
FlightRecorderMXBean fr = JMX.newMXBeanProxy(ManagementFactory.getPlatformMBeanServer(), runtimeName, FlightRecorderMXBean.class, false);
fr.getConfigurations();
}
@Test
public void testQueueAuthorization() throws Exception {
final SimpleString ADDRESS = new SimpleString("address");
final SimpleString QUEUE_A = new SimpleString("a");
final SimpleString QUEUE_B = new SimpleString("b");
Set<Role> aRoles = new HashSet<>();
aRoles.add(new Role(QUEUE_A.toString(), false, true, true, false, false, false, false, false, true, false, true, false));
server.getConfiguration().putSecurityRoles("jmx.queue." + QUEUE_A + ".countMessages", aRoles);
Set<Role> bRoles = new HashSet<>();
bRoles.add(new Role(QUEUE_B.toString(), false, true, true, false, false, false, false, false, true, false, true, false));
server.getConfiguration().putSecurityRoles("jmx.queue." + QUEUE_B + ".countMessages", bRoles);
server.start();
server.addAddressInfo(new AddressInfo(ADDRESS, RoutingType.ANYCAST));
server.createQueue(new QueueConfiguration(QUEUE_A).setAddress(ADDRESS).setRoutingType(RoutingType.ANYCAST));
server.createQueue(new QueueConfiguration(QUEUE_B).setAddress(ADDRESS).setRoutingType(RoutingType.ANYCAST));
QueueControl queueControlA = JMX.newMBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
ObjectNameBuilder.DEFAULT.getQueueObjectName(ADDRESS, QUEUE_A, RoutingType.ANYCAST), QueueControl.class, false);
QueueControl queueControlB = JMX.newMBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
ObjectNameBuilder.DEFAULT.getQueueObjectName(ADDRESS, QUEUE_B, RoutingType.ANYCAST), QueueControl.class, false);
Subject subjectA = new Subject();
subjectA.getPrincipals().add(new UserPrincipal("a"));
subjectA.getPrincipals().add(new RolePrincipal("a"));
Subject subjectB = new Subject();
subjectB.getPrincipals().add(new UserPrincipal("b"));
subjectB.getPrincipals().add(new RolePrincipal("b"));
// client A View queue A
assertEquals(Long.valueOf(0), Subject.doAs(subjectA, new PrivilegedExceptionAction<Long>() {
@Override
public Long run() throws Exception {
return queueControlA.countMessages();
}
}));
// client B view queue A
try {
assertEquals(Long.valueOf(0), Subject.doAs(subjectB, new PrivilegedExceptionAction<Long>() {
@Override
public Long run() throws Exception {
return queueControlA.countMessages();
}
}));
Assert.fail("should throw exception here");
} catch (Exception e) {
assertTrue(e instanceof SecurityException);
}
// client B View queue B
assertEquals(Long.valueOf(0), Subject.doAs(subjectB, new PrivilegedExceptionAction<Long>() {
@Override
public Long run() throws Exception {
return queueControlB.countMessages();
}
}));
// client A View queue B
try {
assertEquals(Long.valueOf(0), Subject.doAs(subjectA, new PrivilegedExceptionAction<Long>() {
@Override
public Long run() throws Exception {
return queueControlB.countMessages();
}
}));
Assert.fail("should throw exception here");
} catch (Exception e) {
assertTrue(e instanceof SecurityException);
}
}
}

View File

@ -175,7 +175,7 @@ public class LDAPSecurityTest extends AbstractLdapTestUnit {
final SimpleString NON_DURABLE_QUEUE = new SimpleString("nonDurableQueue");
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, false, false, false, false, false, false));
roles.add(new Role("programmers", false, false, false, false, false, false, false, false, false, false, false, false));
server.getConfiguration().putSecurityRoles("#", roles);
server.start();
server.createQueue(new QueueConfiguration(DURABLE_QUEUE).setAddress(ADDRESS));
@ -261,7 +261,7 @@ public class LDAPSecurityTest extends AbstractLdapTestUnit {
final SimpleString NON_DURABLE_QUEUE = new SimpleString("nonDurableQueue");
Set<Role> roles = new HashSet<>();
roles.add(new Role("admins", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("admins", true, true, true, true, true, true, true, true, true, true, false, false));
server.getConfiguration().putSecurityRoles("#", roles);
server.start();

View File

@ -59,10 +59,10 @@ public class MultiThreadedAuditLoggingTest extends ActiveMQTestBase {
server.setSecurityManager(new ActiveMQBasicSecurityManager());
server.start();
Set<Role> roles = new HashSet<>();
roles.add(new Role("queue1", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("queue1", true, true, true, true, true, true, true, true, true, true, false, false));
server.getSecurityRepository().addMatch("queue1", roles);
roles = new HashSet<>();
roles.add(new Role("queue2", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("queue2", true, true, true, true, true, true, true, true, true, true, false, false));
server.getSecurityRepository().addMatch("queue2", roles);
server.getActiveMQServerControl().addUser("queue1", "queue1", "queue1", true);
server.getActiveMQServerControl().addUser("queue2", "queue2", "queue2", true);

View File

@ -218,10 +218,10 @@ public class AmqpClientTestSupport extends AmqpTestSupport {
// Configure roles
HierarchicalRepository<Set<Role>> securityRepository = server.getSecurityRepository();
HashSet<Role> value = new HashSet<>();
value.add(new Role("nothing", false, false, false, false, false, false, false, false, false, false));
value.add(new Role("browser", false, false, false, false, false, false, false, true, false, false));
value.add(new Role("guest", false, true, false, false, false, false, false, true, false, false));
value.add(new Role("full", true, true, true, true, true, true, true, true, true, true));
value.add(new Role("nothing", false, false, false, false, false, false, false, false, false, false, false, false));
value.add(new Role("browser", false, false, false, false, false, false, false, true, false, false, false, false));
value.add(new Role("guest", false, true, false, false, false, false, false, true, false, false, false, false));
value.add(new Role("full", true, true, true, true, true, true, true, true, true, true, false, false));
securityRepository.addMatch(getQueueName(), value);
for (String match : securityMatches) {

View File

@ -114,7 +114,7 @@ public class JMSSaslExternalTest extends ActiveMQTestBase {
// role mapping via CertLogin - TextFileCertificateLoginModule
final String roleName = "widgets";
Role role = new Role(roleName, true, true, true, true, true, true, true, true, true, true);
Role role = new Role(roleName, true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch("TEST", roles);

View File

@ -161,7 +161,7 @@ public class JMSSaslGssapiTest extends JMSClientTestSupport {
securityManager.setConfiguration(null);
final String roleName = "ALLOW_ALL";
Role role = new Role(roleName, true, true, true, true, true, true, true, true, true, true);
Role role = new Role(roleName, true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(getQueueName().toString(), roles);

View File

@ -207,7 +207,7 @@ public class AutoCreateJmsDestinationTest extends JMSTestBase {
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().addUser("guest", "guest");
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().setDefaultUser("guest");
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().addRole("guest", "rejectAll");
Role role = new Role("rejectAll", false, false, false, false, false, false, false, false, false, false);
Role role = new Role("rejectAll", false, false, false, false, false, false, false, false, false, false, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch("#", roles);
@ -481,7 +481,7 @@ public class AutoCreateJmsDestinationTest extends JMSTestBase {
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().addUser("guest", "guest");
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().setDefaultUser("guest");
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().addRole("guest", "allowAll");
Role role = new Role("allowAll", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("allowAll", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch("#", roles);

View File

@ -214,7 +214,7 @@ public class CoreClientTest extends ActiveMQTestBase {
server.start();
Role myRole = new Role("myrole", true, true, true, true, true, true, true, true, true, true);
Role myRole = new Role("myrole", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> anySet = new HashSet<>();
anySet.add(myRole);
server.getSecurityRepository().addMatch(baseAddress, anySet);

View File

@ -103,7 +103,7 @@ public class SecurityFailoverTest extends FailoverTest {
protected ActiveMQJAASSecurityManager installSecurity(TestableServer server) {
ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getServer().getSecurityManager();
securityManager.getConfiguration().addUser("a", "b");
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getServer().getSecurityRepository().addMatch("#", roles);

View File

@ -48,6 +48,7 @@ import org.apache.activemq.artemis.core.postoffice.QueueBinding;
import org.apache.activemq.artemis.core.postoffice.impl.DivertBinding;
import org.apache.activemq.artemis.core.postoffice.impl.LocalQueueBinding;
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
import org.apache.activemq.artemis.core.security.CheckType;
import org.apache.activemq.artemis.core.security.Role;
import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType;
import org.apache.activemq.artemis.core.server.cluster.impl.RemoteQueueBindingImpl;
@ -265,7 +266,7 @@ public class RedeployTest extends ActiveMQTestBase {
roles = embeddedActiveMQ.getActiveMQServer().getSecurityRepository().getMatch("foo");
found = false;
for (Role role : roles) {
if (role.getName().equals("b")) {
if (role.getName().equals("b") && CheckType.VIEW.hasRole(role)) {
found = true;
}
}
@ -307,7 +308,7 @@ public class RedeployTest extends ActiveMQTestBase {
assertTrue(found);
embeddedActiveMQ.getActiveMQServer().getActiveMQServerControl().addSecuritySettings("bar", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c");
embeddedActiveMQ.getActiveMQServer().getActiveMQServerControl().addSecuritySettings("bar", "c", "c", "c", "c", "c", "c", "c", "c", "c", "c", "", "");
roles = embeddedActiveMQ.getActiveMQServer().getSecurityRepository().getMatch("bar");
for (Role role : roles) {
if (role.getName().equals("c")) {
@ -1055,9 +1056,9 @@ public class RedeployTest extends ActiveMQTestBase {
@Test
public void testRedeployWithFailover() throws Exception {
Set<Role> original = new HashSet<>();
original.add(new Role("a", false, true, false, false, false, false, false, false, false, false));
original.add(new Role("a", false, true, false, false, false, false, false, false, false, false, false, false));
Set<Role> changed = new HashSet<>();
changed.add(new Role("b", false, true, false, false, false, false, false, false, false, false));
changed.add(new Role("b", false, true, false, false, false, false, false, false, false, false, false, false));

View File

@ -575,7 +575,7 @@ public class SimpleJNDIClientTest extends ActiveMQTestBase {
//setup user and role on broker
((ActiveMQJAASSecurityManager) liveService.getSecurityManager()).getConfiguration().addUser("myUser", "myPassword");
((ActiveMQJAASSecurityManager) liveService.getSecurityManager()).getConfiguration().addRole("myUser", "consumeCreateRole");
Role consumeCreateRole = new Role("consumeCreateRole", false, true, true, true, true, true, true, true, true, true);
Role consumeCreateRole = new Role("consumeCreateRole", false, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> consumerCreateRoles = new HashSet<>();
consumerCreateRoles.add(consumeCreateRole);
liveService.getSecurityRepository().addMatch("test.queue", consumerCreateRoles);

View File

@ -329,7 +329,7 @@ public class TemporaryDestinationTest extends JMSTestBase {
ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager();
securityManager.getConfiguration().addUser("IDo", "Exist");
securityManager.getConfiguration().addRole("IDo", "myrole");
Role myRole = new Role("myrole", true, true, true, true, true, true, true, true, true, true);
Role myRole = new Role("myrole", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> anySet = new HashSet<>();
anySet.add(myRole);
server.getSecurityRepository().addMatch("#", anySet);

View File

@ -238,10 +238,10 @@ public abstract class MultiprotocolJMSClientTestSupport extends ActiveMQTestBase
// Configure roles
HierarchicalRepository<Set<Role>> securityRepository = server.getSecurityRepository();
HashSet<Role> value = new HashSet<>();
value.add(new Role("nothing", false, false, false, false, false, false, false, false, false, false));
value.add(new Role("browser", false, false, false, false, false, false, false, true, false, false));
value.add(new Role("guest", false, true, false, false, false, false, false, true, false, false));
value.add(new Role("full", true, true, true, true, true, true, true, true, true, true));
value.add(new Role("nothing", false, false, false, false, false, false, false, false, false, false, false, false));
value.add(new Role("browser", false, false, false, false, false, false, false, true, false, false, false, false));
value.add(new Role("guest", false, true, false, false, false, false, false, true, false, false, false, false));
value.add(new Role("full", true, true, true, true, true, true, true, true, true, true, false, false));
securityRepository.addMatch("#", value);
for (String match : securityMatches) {

View File

@ -153,7 +153,7 @@ public class ServerLargeMessageTest extends ActiveMQTestBase {
ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false));
server.getConfiguration().setPopulateValidatedUser(true);
Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch("#", roles);

View File

@ -238,7 +238,7 @@ public class ActiveMQServerControlTest extends ManagementTestBase {
ActiveMQServerControl serverControl = createManagementControl();
Wait.assertEquals(usingCore() ? 1 : 0, serverControl::getAuthenticationCacheSize);
Wait.assertEquals(usingCore() ? 7 : 0, serverControl::getAuthorizationCacheSize);
Wait.assertEquals(0, serverControl::getAuthorizationCacheSize);
ServerLocator loc = createInVMNonHALocator();
ClientSessionFactory csf = createSessionFactory(loc);
@ -256,7 +256,7 @@ public class ActiveMQServerControlTest extends ManagementTestBase {
producer.send(m);
Assert.assertEquals(usingCore() ? 2 : 1, serverControl.getAuthenticationCacheSize());
Wait.assertEquals(usingCore() ? 8 : 1, () -> serverControl.getAuthorizationCacheSize());
Wait.assertEquals(1, () -> serverControl.getAuthorizationCacheSize());
}
@Test
@ -289,7 +289,7 @@ public class ActiveMQServerControlTest extends ManagementTestBase {
serverControl.clearAuthorizationCache();
Assert.assertEquals(usingCore() ? 1 : 0, serverControl.getAuthenticationCacheSize());
Assert.assertEquals(usingCore() ? 7 : 0, serverControl.getAuthorizationCacheSize());
Assert.assertEquals(0, serverControl.getAuthorizationCacheSize());
}
@Test
@ -1061,7 +1061,7 @@ public class ActiveMQServerControlTest extends ManagementTestBase {
String exactAddress = "test.whatever";
assertEquals(1, serverControl.getRoles(addressMatch).length);
serverControl.addSecuritySettings(addressMatch, "foo", "foo, bar", null, "bar", "foo, bar", "", "", "bar", "foo", "foo");
serverControl.addSecuritySettings(addressMatch, "foo", "foo, bar", null, "bar", "foo, bar", "", "", "bar", "foo", "foo", "", "");
// Restart the server. Those settings should be persisted
@ -6143,7 +6143,7 @@ public class ActiveMQServerControlTest extends ManagementTestBase {
server.start();
HashSet<Role> role = new HashSet<>();
role.add(new Role("guest", true, true, true, true, true, true, true, true, true, true));
role.add(new Role("guest", true, true, true, true, true, true, true, true, true, true, false, false));
server.getSecurityRepository().addMatch("#", role);
}

View File

@ -938,6 +938,22 @@ public class ActiveMQServerControlUsingCoreTest extends ActiveMQServerControlTes
proxy.invokeOperation("addSecuritySettings", addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles);
}
@Override
public void addSecuritySettings(String addressMatch,
String sendRoles,
String consumeRoles,
String createDurableQueueRoles,
String deleteDurableQueueRoles,
String createNonDurableQueueRoles,
String deleteNonDurableQueueRoles,
String manageRoles,
String browseRoles,
String createAddressRoles,
String deleteAddressRoles) throws Exception {
proxy.invokeOperation("addSecuritySettings", addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles, createAddressRoles, deleteAddressRoles);
}
@Override
public void addSecuritySettings(String addressMatch,
String sendRoles,
@ -949,8 +965,8 @@ public class ActiveMQServerControlUsingCoreTest extends ActiveMQServerControlTes
String manageRoles,
String browseRoles,
String createAddress,
String deleteAddress) throws Exception {
proxy.invokeOperation("addSecuritySettings", addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles, createAddress, deleteAddress);
String deleteAddress, String viewRoles, String editRoles) throws Exception {
proxy.invokeOperation("addSecuritySettings", addressMatch, sendRoles, consumeRoles, createDurableQueueRoles, deleteDurableQueueRoles, createNonDurableQueueRoles, deleteNonDurableQueueRoles, manageRoles, browseRoles, createAddress, deleteAddress, viewRoles, editRoles);
}
@Override

View File

@ -224,7 +224,7 @@ public class AddressControlTest extends ManagementTestBase {
public void testGetRoles() throws Exception {
SimpleString address = RandomUtil.randomSimpleString();
SimpleString queue = RandomUtil.randomSimpleString();
Role role = new Role(RandomUtil.randomString(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean());
Role role = new Role(RandomUtil.randomString(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), false, false);
session.createQueue(new QueueConfiguration(queue).setAddress(address));
@ -260,7 +260,7 @@ public class AddressControlTest extends ManagementTestBase {
public void testGetRolesAsJSON() throws Exception {
SimpleString address = RandomUtil.randomSimpleString();
SimpleString queue = RandomUtil.randomSimpleString();
Role role = new Role(RandomUtil.randomString(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean());
Role role = new Role(RandomUtil.randomString(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), RandomUtil.randomBoolean(), false, false);
session.createQueue(new QueueConfiguration(queue).setAddress(address));

View File

@ -57,7 +57,7 @@ public class ManagementServiceImplTest extends ActiveMQTestBase {
CoreMessage message = new CoreMessage(1, 100);
ManagementHelper.putOperationInvocation(message, ResourceNames.BROKER, "createQueue", queue, address);
Message reply = server.getManagementService().handleMessage(message);
Message reply = server.getManagementService().handleMessage(null, message);
Assert.assertTrue(ManagementHelper.hasOperationSucceeded(reply));
}
@ -73,7 +73,7 @@ public class ManagementServiceImplTest extends ActiveMQTestBase {
CoreMessage message = new CoreMessage(1, 100);
ManagementHelper.putOperationInvocation(message, ResourceNames.BROKER, "thereIsNoSuchOperation");
ICoreMessage reply = server.getManagementService().handleMessage(message);
ICoreMessage reply = server.getManagementService().handleMessage(null, message);
Assert.assertFalse(ManagementHelper.hasOperationSucceeded(reply));
Assert.assertNotNull(ManagementHelper.getResult(reply));
@ -90,7 +90,7 @@ public class ManagementServiceImplTest extends ActiveMQTestBase {
ICoreMessage message = new CoreMessage(1, 100);
ManagementHelper.putOperationInvocation(message, "Resouce.Does.Not.Exist", "toString");
ICoreMessage reply = server.getManagementService().handleMessage(message);
ICoreMessage reply = server.getManagementService().handleMessage(null, message);
Assert.assertFalse(ManagementHelper.hasOperationSucceeded(reply));
Assert.assertNotNull(ManagementHelper.getResult(reply));
@ -108,7 +108,7 @@ public class ManagementServiceImplTest extends ActiveMQTestBase {
ManagementHelper.putAttribute(message, ResourceNames.BROKER, "started");
ICoreMessage reply = server.getManagementService().handleMessage(message);
ICoreMessage reply = server.getManagementService().handleMessage(null, message);
Assert.assertTrue(ManagementHelper.hasOperationSucceeded(reply));
Assert.assertTrue((Boolean) ManagementHelper.getResult(reply));
@ -126,7 +126,7 @@ public class ManagementServiceImplTest extends ActiveMQTestBase {
ManagementHelper.putAttribute(message, ResourceNames.BROKER, "attribute.Does.Not.Exist");
ICoreMessage reply = server.getManagementService().handleMessage(message);
ICoreMessage reply = server.getManagementService().handleMessage(null, message);
Assert.assertFalse(ManagementHelper.hasOperationSucceeded(reply));
Assert.assertNotNull(ManagementHelper.getResult(reply));
@ -172,7 +172,7 @@ public class ManagementServiceImplTest extends ActiveMQTestBase {
MessageUtil.setJMSCorrelationID(message, correlationID);
ManagementHelper.putOperationInvocation(message, ResourceNames.BROKER, "createQueue", queue, address);
Message reply = server.getManagementService().handleMessage(message);
Message reply = server.getManagementService().handleMessage(null, message);
Assert.assertTrue(ManagementHelper.hasOperationSucceeded(reply));
Assert.assertEquals(correlationID, MessageUtil.getJMSCorrelationID(reply));
}
@ -193,7 +193,7 @@ public class ManagementServiceImplTest extends ActiveMQTestBase {
message.setUserID(messageId);
ManagementHelper.putOperationInvocation(message, ResourceNames.BROKER, "createQueue", queue, address);
Message reply = server.getManagementService().handleMessage(message);
Message reply = server.getManagementService().handleMessage(null, message);
Assert.assertTrue(ManagementHelper.hasOperationSucceeded(reply));
Assert.assertEquals(messageId.toString(), MessageUtil.getJMSCorrelationID(reply));
}

View File

@ -68,9 +68,9 @@ public class MessageAuthorizationTest extends ActiveMQTestBase {
server = addServer(ActiveMQServers.newActiveMQServer(createDefaultNettyConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, true));
server.getConfiguration().setPopulateValidatedUser(true);
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("a", false, true, true, true, true, false, false, false, true, true));
roles.add(new Role("b", false, true, true, true, true, false, false, false, true, true));
roles.add(new Role("programmers", true, true, true, true, true, true, true, true, true, true, false, false));
roles.add(new Role("a", false, true, true, true, true, false, false, false, true, true, false, false));
roles.add(new Role("b", false, true, true, true, true, false, false, false, true, true, false, false));
server.getConfiguration().putSecurityRoles("#", roles);
BrokerMessageAuthorizationPlugin plugin = new BrokerMessageAuthorizationPlugin();

View File

@ -114,7 +114,7 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
Role role = new Role("notif", true, true, true, true, false, true, true, true, true, true);
Role role = new Role("notif", true, true, true, true, false, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
@ -153,7 +153,7 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
@Test
public void testCONNECTION_CREATED() throws Exception {
Role role = new Role("notif", true, true, true, true, false, true, true, true, true, true);
Role role = new Role("notif", true, true, true, true, false, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch("#", roles);
@ -208,7 +208,7 @@ public class SSLSecurityNotificationTest extends ActiveMQTestBase {
notifQueue = RandomUtil.randomSimpleString();
Role role = new Role("notif", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("notif", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(ActiveMQDefaultConfiguration.getDefaultManagementNotificationAddress().toString(), roles);

View File

@ -0,0 +1,114 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.activemq.artemis.tests.integration.management;
import java.util.HashSet;
import java.util.Set;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.core.config.Configuration;
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.settings.HierarchicalRepository;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.junit.Test;
public class SecurityManagementMessageRbacTest extends SecurityManagementTestBase {
private final String password = "bla";
private final String guest = "guest";
private final String view = "view";
private final String updater = "updaterUser";
private final String admin = "validAdminUser";
@Test
public void testSendManagementMessageWithAdminRole() throws Exception {
doSendBrokerManagementMessage(admin, password, true);
}
@Test
public void testSendManagementMessageAsGuest() throws Exception {
doSendBrokerManagementMessage(guest, password, false);
}
@Test
public void testSendManagementMessageAsView() throws Exception {
doSendBrokerManagementMessage(view, password, true);
}
@Test
public void testSendManagementOpWithView() throws Exception {
doSendBrokerManagementMessageFor(false, view, password, false);
}
@Test
public void testSendManagementOpWithGuest() throws Exception {
doSendBrokerManagementMessageFor(false, guest, password, false);
}
@Test
public void testSendManagementOpWithUpdateRole() throws Exception {
doSendBrokerManagementMessageFor(false, updater, password, true);
}
@Override
protected ActiveMQServer setupAndStartActiveMQServer() throws Exception {
Configuration config = createDefaultInVMConfig().setSecurityEnabled(true);
config.setManagementMessageRbac(true); // enable rbac view/update perms check
config.setManagementRbacPrefix("mm");
ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(config, false));
server.start();
HierarchicalRepository<Set<Role>> securityRepository = server.getSecurityRepository();
ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager();
securityManager.getConfiguration().addUser(admin, password);
securityManager.getConfiguration().addUser(guest, password);
securityManager.getConfiguration().addUser(updater, password);
securityManager.getConfiguration().addUser(view, password);
securityManager.getConfiguration().addRole(admin, "manageRole");
securityManager.getConfiguration().addRole(admin, "updateRole");
securityManager.getConfiguration().addRole(guest, "guestRole");
securityManager.getConfiguration().addRole(view, "viewRole");
securityManager.getConfiguration().addRole(view, "manageRole");
securityManager.getConfiguration().addRole(updater, "updateRole");
securityManager.getConfiguration().addRole(updater, "manageRole");
Set<Role> permissionsOnManagementAddress = new HashSet<>();
permissionsOnManagementAddress.add(new Role("manageRole", true, true, false, false, true, true, true, true, true, true, false, false));
securityRepository.addMatch(ActiveMQDefaultConfiguration.getDefaultManagementAddress().toString() + ".*", permissionsOnManagementAddress); // for create reply queue
securityRepository.addMatch(ActiveMQDefaultConfiguration.getDefaultManagementAddress().toString(), permissionsOnManagementAddress); // for send to manage address
Set<Role> catchAllPermissions = new HashSet<>();
catchAllPermissions.add(new Role("guestRole", false, false, false, false, false, false, false, false, false, false, false, false));
securityRepository.addMatch("#", catchAllPermissions);
final String brokerControlRbacAttributeKey = "mm.broker.isStarted";
Set<Role> brokerControlRoles = new HashSet<>();
brokerControlRoles.add(new Role("viewRole", true, true, true, true, true, true, true, true, true, true, true, false));
brokerControlRoles.add(new Role("updateRole", true, true, true, true, true, true, true, true, true, true, true, true));
securityRepository.addMatch(brokerControlRbacAttributeKey, brokerControlRoles);
final String brokerControlRbacOpKey = "mm.broker.enableMessageCounters";
securityRepository.addMatch(brokerControlRbacOpKey, brokerControlRoles);
return server;
}
}

View File

@ -16,7 +16,12 @@
*/
package org.apache.activemq.artemis.tests.integration.management;
import java.util.Arrays;
import java.util.Collection;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.ActiveMQClusterSecurityException;
import org.apache.activemq.artemis.api.core.ActiveMQSecurityException;
import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientRequestor;
import org.apache.activemq.artemis.api.core.client.ClientSession;
@ -24,18 +29,24 @@ import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
import org.apache.activemq.artemis.api.core.management.ResourceNames;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Assert;
import org.junit.Before;
import org.junit.runners.Parameterized;
public abstract class SecurityManagementTestBase extends ActiveMQTestBase {
private ActiveMQServer server;
@Parameterized.Parameter(0)
public boolean managementRbac = false;
@Parameterized.Parameters(name = "managementRbac={0}")
public static Collection<Object[]> getParams() {
return Arrays.asList(new Object[][]{{true}, {false}});
}
@Override
@ -48,9 +59,31 @@ public abstract class SecurityManagementTestBase extends ActiveMQTestBase {
protected abstract ActiveMQServer setupAndStartActiveMQServer() throws Exception;
protected void doSendManagementMessage(final String user,
final String password,
final boolean expectSuccess) throws Exception {
@Override
protected Configuration createDefaultInVMConfig() throws Exception {
Configuration configuration = super.createDefaultInVMConfig();
if (managementRbac) {
configuration.setManagementMessageRbac(true); // enable rbac view/update perms check
}
return configuration;
}
protected void doSendBrokerManagementMessage(final String user,
final String password,
final boolean expectSuccess) throws Exception {
doSendBrokerManagementMessageForAttribute(user, password, expectSuccess);
}
protected void doSendBrokerManagementMessageForAttribute(final String user,
final String password,
final boolean expectSuccess) throws Exception {
doSendBrokerManagementMessageFor(true, user, password, expectSuccess);
}
protected void doSendBrokerManagementMessageFor(final boolean attribute, final String user,
final String password,
final boolean expectSuccess) throws Exception {
ServerLocator locator = createInVMNonHALocator();
ClientSessionFactory sf = locator.createSessionFactory();
try {
@ -66,26 +99,40 @@ public abstract class SecurityManagementTestBase extends ActiveMQTestBase {
ClientRequestor requestor = new ClientRequestor(session, ActiveMQDefaultConfiguration.getDefaultManagementAddress());
ClientMessage mngmntMessage = session.createMessage(false);
ManagementHelper.putAttribute(mngmntMessage, ResourceNames.BROKER, "started");
if (attribute) {
ManagementHelper.putAttribute(mngmntMessage, ResourceNames.BROKER, "started");
} else {
ManagementHelper.putOperationInvocation(mngmntMessage, ResourceNames.BROKER, "enableMessageCounters");
}
ClientMessage reply = requestor.request(mngmntMessage, 500);
if (expectSuccess) {
Assert.assertNotNull(reply);
Assert.assertTrue((Boolean) ManagementHelper.getResult(reply));
Assert.assertTrue("" + ManagementHelper.getResult(reply), (Boolean) ManagementHelper.hasOperationSucceeded(reply));
if (attribute) {
Assert.assertTrue("" + ManagementHelper.getResult(reply), (Boolean) ManagementHelper.getResult(reply));
}
} else {
Assert.assertNull(reply);
if (attribute) {
Assert.assertNull(reply);
} else {
Assert.assertNotNull(reply);
Assert.assertFalse("" + ManagementHelper.getResult(reply), (Boolean) ManagementHelper.hasOperationSucceeded(reply));
}
}
requestor.close();
} catch (Exception e) {
} catch (ActiveMQSecurityException possiblyExpected) {
if (expectSuccess) {
Assert.fail("got unexpected exception " + e.getClass() + ": " + e.getMessage());
e.printStackTrace();
Assert.fail("got unexpected security exception " + possiblyExpected.getClass() + ": " + possiblyExpected.getMessage());
}
} catch (ActiveMQClusterSecurityException possiblyExpected) {
if (expectSuccess) {
Assert.fail("got unexpected security exception " + possiblyExpected.getClass() + ": " + possiblyExpected.getMessage());
}
} catch (Exception e) {
Assert.fail("got unexpected exception " + e.getClass() + ": " + e.getMessage());
} finally {
sf.close();
}
}
}

View File

@ -26,7 +26,10 @@ import org.apache.activemq.artemis.core.server.ActiveMQServers;
import org.apache.activemq.artemis.core.settings.HierarchicalRepository;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class SecurityManagementWithConfiguredAdminUserTest extends SecurityManagementTestBase {
@ -48,22 +51,22 @@ public class SecurityManagementWithConfiguredAdminUserTest extends SecurityManag
*/
@Test
public void testSendManagementMessageWithClusterAdminUser() throws Exception {
doSendManagementMessage(ActiveMQDefaultConfiguration.getDefaultClusterUser(), CLUSTER_PASSWORD, true);
doSendBrokerManagementMessage(ActiveMQDefaultConfiguration.getDefaultClusterUser(), CLUSTER_PASSWORD, true);
}
@Test
public void testSendManagementMessageWithAdminRole() throws Exception {
doSendManagementMessage(validAdminUser, validAdminPassword, true);
doSendBrokerManagementMessage(validAdminUser, validAdminPassword, true);
}
@Test
public void testSendManagementMessageWithoutAdminRole() throws Exception {
doSendManagementMessage(invalidAdminUser, invalidAdminPassword, false);
doSendBrokerManagementMessage(invalidAdminUser, invalidAdminPassword, false);
}
@Test
public void testSendManagementMessageWithoutUserCredentials() throws Exception {
doSendManagementMessage(null, null, false);
doSendBrokerManagementMessage(null, null, false);
}
@ -83,10 +86,10 @@ public class SecurityManagementWithConfiguredAdminUserTest extends SecurityManag
securityManager.getConfiguration().addRole(invalidAdminUser, "guest");
Set<Role> adminRole = securityRepository.getMatch(ActiveMQDefaultConfiguration.getDefaultManagementAddress().toString());
adminRole.add(new Role("admin", true, true, true, true, true, true, true, true, true, true));
adminRole.add(new Role("admin", true, true, true, true, true, true, true, true, true, true, managementRbac, managementRbac));
securityRepository.addMatch(ActiveMQDefaultConfiguration.getDefaultManagementAddress().toString(), adminRole);
Set<Role> guestRole = securityRepository.getMatch("*");
guestRole.add(new Role("guest", true, true, true, true, true, true, false, true, true, true));
guestRole.add(new Role("guest", true, true, true, true, true, true, false, true, true, true, false, false));
securityRepository.addMatch("*", guestRole);
return server;

View File

@ -21,22 +21,25 @@ import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class SecurityManagementWithDefaultConfigurationTest extends SecurityManagementTestBase {
@Test
public void testSendManagementMessageWithDefaultClusterAdminUser() throws Exception {
doSendManagementMessage(ActiveMQDefaultConfiguration.getDefaultClusterUser(), ActiveMQDefaultConfiguration.getDefaultClusterPassword(), true);
doSendBrokerManagementMessage(ActiveMQDefaultConfiguration.getDefaultClusterUser(), ActiveMQDefaultConfiguration.getDefaultClusterPassword(), true);
}
@Test
public void testSendManagementMessageWithGuest() throws Exception {
doSendManagementMessage("guest", "guest", false);
doSendBrokerManagementMessage("guest", "guest", false);
}
@Test
public void testSendManagementMessageWithoutUserCredentials() throws Exception {
doSendManagementMessage(null, null, false);
doSendBrokerManagementMessage(null, null, false);
}

View File

@ -21,7 +21,10 @@ import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.server.ActiveMQServer;
import org.apache.activemq.artemis.core.server.ActiveMQServers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class SecurityManagementWithModifiedConfigurationTest extends SecurityManagementTestBase {
@ -31,22 +34,22 @@ public class SecurityManagementWithModifiedConfigurationTest extends SecurityMan
@Test
public void testSendManagementMessageWithModifiedClusterAdminUser() throws Exception {
doSendManagementMessage(ActiveMQDefaultConfiguration.getDefaultClusterUser(), configuredClusterPassword, true);
doSendBrokerManagementMessage(ActiveMQDefaultConfiguration.getDefaultClusterUser(), configuredClusterPassword, true);
}
@Test
public void testSendManagementMessageWithDefaultClusterAdminUser() throws Exception {
doSendManagementMessage(ActiveMQDefaultConfiguration.getDefaultClusterUser(), ActiveMQDefaultConfiguration.getDefaultClusterPassword(), false);
doSendBrokerManagementMessage(ActiveMQDefaultConfiguration.getDefaultClusterUser(), ActiveMQDefaultConfiguration.getDefaultClusterPassword(), false);
}
@Test
public void testSendManagementMessageWithGuest() throws Exception {
doSendManagementMessage("guest", "guest", false);
doSendBrokerManagementMessage("guest", "guest", false);
}
@Test
public void testSendManagementMessageWithoutUserCredentials() throws Exception {
doSendManagementMessage(null, null, false);
doSendBrokerManagementMessage(null, null, false);
}

View File

@ -16,6 +16,10 @@
*/
package org.apache.activemq.artemis.tests.integration.management;
import javax.management.JMX;
import javax.security.auth.Subject;
import java.lang.management.ManagementFactory;
import java.security.PrivilegedExceptionAction;
import java.util.HashSet;
import java.util.Set;
@ -29,13 +33,16 @@ import org.apache.activemq.artemis.api.core.client.ClientMessage;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.api.core.management.AddressControl;
import org.apache.activemq.artemis.api.core.management.ManagementHelper;
import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder;
import org.apache.activemq.artemis.core.config.Configuration;
import org.apache.activemq.artemis.core.security.CheckType;
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.spi.core.security.ActiveMQJAASSecurityManager;
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.apache.activemq.artemis.utils.RandomUtil;
import org.junit.Assert;
@ -91,7 +98,7 @@ public class SecurityNotificationTest extends ActiveMQTestBase {
SimpleString address = RandomUtil.randomSimpleString();
// guest can not create queue
Role role = new Role("roleCanNotCreateQueue", true, true, false, true, false, true, true, true, true, true);
Role role = new Role("roleCanNotCreateQueue", true, true, false, true, false, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(address.toString(), roles);
@ -131,12 +138,54 @@ public class SecurityNotificationTest extends ActiveMQTestBase {
guestSession.close();
}
@Test
public void testSubjectSECURITY_PERMISSION_VIOLATION() throws Exception {
SecurityNotificationTest.flush(notifConsumer);
Subject guestSubject = new Subject();
guestSubject.getPrincipals().add(new UserPrincipal("guest"));
final AddressControl addressControl = JMX.newMBeanProxy(
ManagementFactory.getPlatformMBeanServer(),
ObjectNameBuilder.DEFAULT.getAddressObjectName(ActiveMQDefaultConfiguration.getDefaultManagementNotificationAddress()), AddressControl.class, false);
Exception e = Subject.doAs(guestSubject, new PrivilegedExceptionAction<Exception>() {
@Override
public Exception run() throws Exception {
try {
addressControl.sendMessage(null, 1, "hi", false, null, null);
fail("need Send permission");
} catch (Exception expected) {
assertTrue(expected.getMessage().contains("guest"));
assertTrue(expected.getMessage().contains("SEND"));
return expected;
}
return null;
}
});
assertNotNull("expect exception", e);
ClientMessage[] notifications = SecurityNotificationTest.consumeMessages(3, notifConsumer);
int i = 0;
for (i = 0; i < notifications.length; i++) {
if (SECURITY_PERMISSION_VIOLATION.toString().equals(notifications[i].getObjectProperty(ManagementHelper.HDR_NOTIFICATION_TYPE).toString())) {
break;
}
}
Assert.assertTrue(i < notifications.length);
Assert.assertEquals(SECURITY_PERMISSION_VIOLATION.toString(), notifications[i].getObjectProperty(ManagementHelper.HDR_NOTIFICATION_TYPE).toString());
Assert.assertEquals("guest", notifications[i].getObjectProperty(ManagementHelper.HDR_USER).toString());
Assert.assertEquals(ActiveMQDefaultConfiguration.getDefaultManagementNotificationAddress().toString(), notifications[i].getObjectProperty(ManagementHelper.HDR_ADDRESS).toString());
Assert.assertEquals(CheckType.SEND.toString(), notifications[i].getObjectProperty(ManagementHelper.HDR_CHECK_TYPE).toString());
}
@Test
public void testCONSUMER_CREATED() throws Exception {
SimpleString queue = RandomUtil.randomSimpleString();
SimpleString address = RandomUtil.randomSimpleString();
Role role = new Role("role", true, true, true, true, false, true, true, true, true, true);
Role role = new Role("role", true, true, true, true, false, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(address.toString(), roles);
@ -173,7 +222,7 @@ public class SecurityNotificationTest extends ActiveMQTestBase {
public void setUp() throws Exception {
super.setUp();
Configuration config = createDefaultInVMConfig().setSecurityEnabled(true);
Configuration config = createDefaultInVMConfig().setSecurityEnabled(true).setJMXManagementEnabled(true);
server = addServer(ActiveMQServers.newActiveMQServer(config, false));
server.start();
@ -184,7 +233,7 @@ public class SecurityNotificationTest extends ActiveMQTestBase {
securityManager.getConfiguration().addUser("guest", "guest");
securityManager.getConfiguration().setDefaultUser("guest");
Role role = new Role("notif", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("notif", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(ActiveMQDefaultConfiguration.getDefaultManagementNotificationAddress().toString(), roles);

View File

@ -30,6 +30,7 @@ import org.apache.activemq.artemis.spi.core.protocol.ProtocolManager;
import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection;
import org.apache.activemq.artemis.spi.core.remoting.Acceptor;
import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager5;
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal;
import org.apache.activemq.artemis.tests.util.RandomUtil;
import org.apache.activemq.artemis.tests.util.Wait;
import org.fusesource.mqtt.client.BlockingConnection;
@ -61,7 +62,9 @@ public class MQTTSecurityManagerTest extends MQTTTestSupport {
throw new InvalidClientIdException();
}
remotingConnection.setClientID(clientID);
return new Subject();
Subject subject = new Subject();
subject.getPrincipals().add(new UserPrincipal(user));
return subject;
}
@Override

View File

@ -198,10 +198,10 @@ public class MQTTTestSupport extends ActiveMQTestBase {
// Configure roles
HierarchicalRepository<Set<Role>> securityRepository = server.getSecurityRepository();
HashSet<Role> value = new HashSet<>();
value.add(new Role("nothing", false, false, false, false, false, false, false, false, false, false));
value.add(new Role("browser", false, false, false, false, false, false, false, true, false, false));
value.add(new Role("guest", false, true, false, false, false, false, false, true, false, false));
value.add(new Role("full", true, true, true, true, true, true, true, true, true, true));
value.add(new Role("nothing", false, false, false, false, false, false, false, false, false, false, false, false));
value.add(new Role("browser", false, false, false, false, false, false, false, true, false, false, false, false));
value.add(new Role("guest", false, true, false, false, false, false, false, true, false, false, false, false));
value.add(new Role("full", true, true, true, true, true, true, true, true, true, true, false, false));
securityRepository.addMatch(MQTTUtil.getCoreAddressFromMqttTopic(getQueueName(), server.getConfiguration().getWildcardConfiguration()), value);
server.getConfiguration().setSecurityEnabled(true);

View File

@ -52,7 +52,7 @@ public class PahoMQTTQOS2SecurityTest extends MQTTTestSupport {
// Configure roles
HierarchicalRepository<Set<Role>> securityRepository = server.getSecurityRepository();
HashSet<Role> value = new HashSet<>();
value.add(new Role("addressOnly", true, true, true, true, false, false, false, false, true, true));
value.add(new Role("addressOnly", true, true, true, true, false, false, false, false, true, true, false, false));
securityRepository.addMatch(MQTTUtil.getCoreAddressFromMqttTopic(getQueueName(), server.getConfiguration().getWildcardConfiguration()), value);
}

View File

@ -217,12 +217,12 @@ public class MQTT5TestSupport extends ActiveMQTestBase {
// Configure roles
HierarchicalRepository<Set<Role>> securityRepository = server.getSecurityRepository();
HashSet<Role> value = new HashSet<>();
value.add(new Role("nothing", false, false, false, false, false, false, false, false, false, false));
value.add(new Role("browser", false, false, false, false, false, false, false, true, false, false));
value.add(new Role("guest", false, true, false, false, false, false, false, true, false, false));
value.add(new Role("full", true, true, true, true, true, true, true, true, true, true));
value.add(new Role("createAddress", false, false, false, false, false, false, false, false, true, false));
value.add(new Role("noDelete", true, true, true, false, true, false, true, true, true, true));
value.add(new Role("nothing", false, false, false, false, false, false, false, false, false, false, false, false));
value.add(new Role("browser", false, false, false, false, false, false, false, true, false, false, false, false));
value.add(new Role("guest", false, true, false, false, false, false, false, true, false, false, false, false));
value.add(new Role("full", true, true, true, true, true, true, true, true, true, true, false, false));
value.add(new Role("createAddress", false, false, false, false, false, false, false, false, true, false, false, false));
value.add(new Role("noDelete", true, true, true, false, true, false, true, true, true, true, false, false));
securityRepository.addMatch("#", value);
server.getConfiguration().setSecurityEnabled(true);

View File

@ -83,7 +83,7 @@ public class CertificateAuthenticationSslTests extends MQTT5TestSupport {
server.setSecurityManager(new ActiveMQJAASSecurityManager("CertLogin"));
server.getConfiguration().setSecurityEnabled(true);
HashSet<Role> roles = new HashSet<>();
roles.add(new Role("programmers", true, true, true, false, false, false, false, false, true, true));
roles.add(new Role("programmers", true, true, true, false, false, false, false, false, true, true, false, false));
server.getConfiguration().putSecurityRoles("#", roles);
}

View File

@ -73,23 +73,23 @@ public class OpenWireTestBase extends ActiveMQTestBase {
securityManager.getConfiguration().addRole("openwireSender", "sender");
securityManager.getConfiguration().addUser("openwireSender", "SeNdEr");
//sender cannot receive
Role senderRole = new Role("sender", true, false, false, false, true, true, false, false, true, true);
Role senderRole = new Role("sender", true, false, false, false, true, true, false, false, true, true, false, false);
securityManager.getConfiguration().addRole("openwireReceiver", "receiver");
securityManager.getConfiguration().addUser("openwireReceiver", "ReCeIvEr");
//receiver cannot send
Role receiverRole = new Role("receiver", false, true, false, false, true, true, false, true, false, false);
Role receiverRole = new Role("receiver", false, true, false, false, true, true, false, true, false, false, false, false);
securityManager.getConfiguration().addRole("openwireGuest", "guest");
securityManager.getConfiguration().addUser("openwireGuest", "GuEsT");
//guest cannot do anything
Role guestRole = new Role("guest", false, false, false, false, false, false, false, false, false, false);
Role guestRole = new Role("guest", false, false, false, false, false, false, false, false, false, false, false, false);
securityManager.getConfiguration().addRole("openwireDestinationManager", "manager");
securityManager.getConfiguration().addUser("openwireDestinationManager", "DeStInAtIoN");
Role destRole = new Role("manager", false, false, false, false, true, true, false, false, true, false);
Role destRole = new Role("manager", false, false, false, false, true, true, false, false, true, false, false, false);
Set<Role> roles = new HashSet<>();
roles.add(senderRole);
@ -101,7 +101,7 @@ public class OpenWireTestBase extends ActiveMQTestBase {
// advisory addresses, anyone can create/consume
// broker can produce
Role advisoryReceiverRole = new Role("advisoryReceiver", false, true, false, false, true, true, false, true, true, false);
Role advisoryReceiverRole = new Role("advisoryReceiver", false, true, false, false, true, true, false, true, true, false, false, false);
roles = new HashSet<>();
roles.add(advisoryReceiverRole);

View File

@ -62,7 +62,7 @@ public class SecurityOpenWireTest extends BasicOpenWireTest {
@Test
public void testSendNoAuth() throws Exception {
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, false, false, false, false, false, false));
roles.add(new Role("programmers", false, false, false, false, false, false, false, false, false, false, false, false));
server.getSecurityRepository().addMatch("denyQ", roles);
SimpleString denyQ = new SimpleString("denyQ");

View File

@ -50,9 +50,9 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
public void testStoreSecuritySettings() throws Exception {
createStorage();
addSetting(new PersistedSecuritySetting("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", null, null));
addSetting(new PersistedSecuritySetting("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", null, null));
journal.stop();
@ -62,9 +62,9 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
checkSettings();
addSetting(new PersistedSecuritySetting("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", null, null));
addSetting(new PersistedSecuritySetting("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", null, null));
checkSettings();
@ -92,7 +92,7 @@ public class RolesConfigurationStorageTest extends StorageManagerTestBase {
checkSettings();
addSetting(new PersistedSecuritySetting("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", null, null));
journal.stop();

View File

@ -0,0 +1,172 @@
/*
* 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.persistence;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.invoke.MethodHandles;
import java.lang.management.ManagementFactory;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;
import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration;
import org.apache.activemq.artemis.api.core.QueueConfiguration;
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.cli.commands.tools.xml.XmlDataExporter;
import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataImporter;
import org.apache.activemq.artemis.core.config.Configuration;
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.Queue;
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
import org.apache.activemq.artemis.tests.integration.security.SecurityTest;
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XmlImportExportRbacTest extends ActiveMQTestBase {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
static {
String path = System.getProperty("java.security.auth.login.config");
if (path == null) {
URL resource = SecurityTest.class.getClassLoader().getResource("login.config");
if (resource != null) {
path = resource.getFile();
System.setProperty("java.security.auth.login.config", path);
}
}
}
public static final int CONSUMER_TIMEOUT = 5000;
private static final String QUEUE_NAME = "A1";
private ServerLocator locator;
private ActiveMQServer server;
private ClientSessionFactory factory;
Set<Role> permissionsOnManagementAddress = new HashSet<>();
private ClientSession basicSetUp() throws Exception {
ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("PropertiesLogin");
Configuration configuration = createDefaultInVMConfig();
configuration.setSecurityEnabled(true);
configuration.setManagementMessageRbac(true);
server = addServer(ActiveMQServers.newActiveMQServer(configuration, ManagementFactory.getPlatformMBeanServer(), securityManager, true));
// our 'first' user is in the 'programmers' role, grant minimal necessary permissions
Set<Role> permissionToProduceAnyMessage = new HashSet<>();
permissionToProduceAnyMessage.add(new Role("programmers", true, true, true, false, false, false, false, false, true, false, false, false));
server.getSecurityRepository().addMatch("A1", permissionToProduceAnyMessage);
permissionsOnManagementAddress.add(new Role("programmers", true, true, true, false, true, true, true, false, true, true, true, false));
server.getSecurityRepository().addMatch(ActiveMQDefaultConfiguration.getDefaultManagementAddress().toString() + ".*", permissionsOnManagementAddress); // for create reply queue
server.getSecurityRepository().addMatch(ActiveMQDefaultConfiguration.getDefaultManagementAddress().toString(), permissionsOnManagementAddress); // for send to manage address
server.start();
locator = createInVMNonHALocator();
factory = createSessionFactory(locator);
return addClientSession(factory.createSession("first", "secret", false, true, true, false, 100));
}
@Test
public void testExportWithOutAndWithQueueControlPerms() throws Exception {
ClientSession session = basicSetUp();
session.createQueue(new QueueConfiguration(QUEUE_NAME));
ClientProducer producer = session.createProducer(QUEUE_NAME);
StringBuilder international = new StringBuilder();
for (char x = 800; x < 1200; x++) {
international.append(x);
}
String special = "\"<>'&";
for (int i = 0; i < 5; i++) {
ClientMessage msg = session.createMessage(true);
msg.getBodyBuffer().writeString("Bob the giant pig " + i);
producer.send(msg);
}
session.close();
locator.close();
server.stop();
ByteArrayOutputStream xmlOutputStream = new ByteArrayOutputStream();
XmlDataExporter xmlDataExporter = new XmlDataExporter();
xmlDataExporter.process(xmlOutputStream, server.getConfiguration().getBindingsDirectory(), server.getConfiguration().getJournalDirectory(), server.getConfiguration().getPagingDirectory(), server.getConfiguration().getLargeMessagesDirectory());
if (logger.isDebugEnabled()) {
logger.debug(new String(xmlOutputStream.toByteArray()));
}
clearDataRecreateServerDirs();
server.start();
locator = createInVMNonHALocator();
factory = createSessionFactory(locator);
session = factory.createSession("first", "secret", false, true, true, false, 100);
ByteArrayInputStream xmlInputStream = new ByteArrayInputStream(xmlOutputStream.toByteArray());
XmlDataImporter xmlDataImporter = new XmlDataImporter();
xmlDataImporter.validate(xmlInputStream);
xmlInputStream.reset();
xmlDataImporter.process(xmlInputStream, session);
// assert messages not present due to no permission
Queue queue = server.locateQueue(QUEUE_NAME);
assertEquals(0L, queue.getMessageCount());
// try again with permission
server.getSecurityRepository().addMatch(SimpleString.toSimpleString(server.getConfiguration().getManagementRbacPrefix()).concat(".queue." + QUEUE_NAME + ".getID").toString(), permissionsOnManagementAddress); // for send to manage address
xmlInputStream = new ByteArrayInputStream(xmlOutputStream.toByteArray());
xmlDataImporter = new XmlDataImporter();
xmlDataImporter.validate(xmlInputStream);
xmlInputStream.reset();
xmlDataImporter.process(xmlInputStream, session);
// should be able to consume and verify with the queue control getAttribute view permission
ClientConsumer consumer = session.createConsumer(QUEUE_NAME);
session.start();
for (int i = 0; i < 5; i++) {
ClientMessage msg = consumer.receive(CONSUMER_TIMEOUT);
byte[] body = new byte[msg.getBodySize()];
msg.getBodyBuffer().readBytes(body);
assertTrue(new String(body).contains("Bob the giant pig " + i));
}
}
}

View File

@ -67,7 +67,7 @@ public class ActiveMQMessageHandlerSecurityTest extends ActiveMQRATestBase {
ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager();
securityManager.getConfiguration().addUser("testuser", "testpassword");
securityManager.getConfiguration().addRole("testuser", "arole");
Role role = new Role("arole", false, true, false, false, false, false, false, false, false, false);
Role role = new Role("arole", false, true, false, false, false, false, false, false, false, false, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles);

View File

@ -57,7 +57,7 @@ public class JMSContextTest extends ActiveMQRATestBase {
securityManager.getConfiguration().setDefaultUser("guest");
securityManager.getConfiguration().addRole("testuser", "arole");
securityManager.getConfiguration().addRole("guest", "arole");
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles);

View File

@ -71,7 +71,7 @@ public class OutgoingConnectionJTATest extends ActiveMQRATestBase {
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().setDefaultUser("guest");
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().addRole("testuser", "arole");
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().addRole("guest", "arole");
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles);

View File

@ -71,7 +71,7 @@ public class OutgoingConnectionNoJTATest extends ActiveMQRATestBase {
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().setDefaultUser("guest");
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().addRole("testuser", "arole");
((ActiveMQJAASSecurityManager) server.getSecurityManager()).getConfiguration().addRole("guest", "arole");
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles);

View File

@ -82,7 +82,7 @@ public class OutgoingConnectionTest extends ActiveMQRATestBase {
securityManager.getConfiguration().setDefaultUser("guest");
securityManager.getConfiguration().addRole("testuser", "arole");
securityManager.getConfiguration().addRole("guest", "arole");
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("arole", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles);

View File

@ -241,7 +241,7 @@ public class KeyTypeTest extends RoutingTestBase {
// ensure advisory permission is present for openwire connection creation by 'b'
HierarchicalRepository<Set<Role>> securityRepository = servers[0].getSecurityRepository();
Role role = new Role("b", true, true, true, true, true, true, false, false, true, true);
Role role = new Role("b", true, true, true, true, true, true, false, false, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
securityRepository.addMatch("ActiveMQ.Advisory.#", roles);

View File

@ -131,7 +131,7 @@ public class BasicSecurityManagerTest extends ActiveMQTestBase {
ActiveMQServer server = initializeServer();
server.getConfiguration().setPopulateValidatedUser(true);
server.start();
Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true);
Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true, false, false);
Set<Role> roles = new HashSet<>();
roles.add(role);
server.getSecurityRepository().addMatch("#", roles);
@ -178,7 +178,7 @@ public class BasicSecurityManagerTest extends ActiveMQTestBase {
ActiveMQServer server = initializeServer();
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", false, false, false, false, false, false, false, false, false, false));
roles.add(new Role("programmers", false, false, false, false, false, false, false, false, false, false, false, false));
server.getConfiguration().putSecurityRoles("#", roles);
server.start();
server.addAddressInfo(new AddressInfo(ADDRESS, RoutingType.ANYCAST));
@ -263,7 +263,7 @@ public class BasicSecurityManagerTest extends ActiveMQTestBase {
ActiveMQServer server = initializeServer();
Set<Role> roles = new HashSet<>();
roles.add(new Role("programmers", true, true, true, true, true, true, true, true, true, true));
roles.add(new Role("programmers", true, true, true, true, true, true, true, true, true, true, false, false));
server.getConfiguration().putSecurityRoles("#", roles);
server.start();

View File

@ -54,8 +54,8 @@ public class FQQNSendSecurityTest extends ActiveMQTestBase {
super.setUp();
Configuration configuration = createDefaultInVMConfig().setSecurityEnabled(true);
RoleSet roles = new RoleSet();
roles.add(new Role(ALLOWED_ROLE, true, false, false, false, false, false, false, false, false, false));
roles.add(new Role(DENIED_ROLE, false, false, false, false, false, false, false, false, false, false));
roles.add(new Role(ALLOWED_ROLE, true, false, false, false, false, false, false, false, false, false, false, false));
roles.add(new Role(DENIED_ROLE, false, false, false, false, false, false, false, false, false, false, false, false));
configuration.putSecurityRoles(CompositeAddress.toFullyQualified(ADDRESS, QUEUE), roles);
ActiveMQServer server = createServer(false, configuration);

View File

@ -40,5 +40,7 @@ public class PersistedSecuritySettingTest {
persistedSecuritySetting.getDeleteNonDurableQueueRoles();
persistedSecuritySetting.getManageRoles();
persistedSecuritySetting.getSendRoles();
persistedSecuritySetting.getViewRoles();
persistedSecuritySetting.getEditRoles();
}
}

Some files were not shown because too many files have changed in this diff Show More