ARTEMIS-4582 - view and edit permissions, mops. security-settings for rbac on management apis
This commit is contained in:
parent
576622571a
commit
2e17a4a007
|
@ -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);
|
||||
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);
|
||||
}
|
||||
if (queueID != -1) {
|
||||
queueIDs.put(queue, queueID); // store it so we don't have to look it up every time
|
||||
}
|
||||
}
|
||||
|
||||
if (queueID != -1) {
|
||||
buffer.putLong(queueID);
|
||||
}
|
||||
if (debugLog) {
|
||||
debugLogMessage.append(queue).append(", ");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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><title>.*</title></token>
|
||||
<value><title>${project.name}</title></value>
|
||||
</replacement>
|
||||
<replacement>
|
||||
<token><load-on-startup>1</load-on-startup></token>
|
||||
<value><load-on-startup>-1</load-on-startup></value>
|
||||
</replacement>
|
||||
</replacements>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 +
|
||||
"]";
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,12 +457,9 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void unregisterBroadcastGroup(final String name) throws Exception {
|
||||
|
@ -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);
|
||||
|
@ -878,13 +902,15 @@ public class ManagementServiceImpl implements ManagementService {
|
|||
@Override
|
||||
public Object invokeOperation(final String resourceName,
|
||||
final String operation,
|
||||
final Object[] params) throws Exception {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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,15 +35,15 @@ import static org.junit.Assert.assertNull;
|
|||
|
||||
public class SecurityStoreImplTest {
|
||||
|
||||
@Test
|
||||
public void zeroCacheSizeTest() throws Exception {
|
||||
ActiveMQSecurityManager5 securityManager = new ActiveMQSecurityManager5() {
|
||||
final ActiveMQSecurityManager5 securityManager = new ActiveMQSecurityManager5() {
|
||||
@Override
|
||||
public Subject authenticate(String user,
|
||||
String password,
|
||||
RemotingConnection remotingConnection,
|
||||
String securityDomain) throws NoCacheLoginException {
|
||||
return new Subject();
|
||||
String securityDomain) {
|
||||
Subject subject = new Subject();
|
||||
subject.getPrincipals().add(new UserPrincipal(user));
|
||||
return subject;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,9 +61,13 @@ public class SecurityStoreImplTest {
|
|||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void zeroCacheSizeTest() throws Exception {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
@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);
|
||||
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 {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue