diff --git a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataImporter.java b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataImporter.java index 31da6f752e1..3447ed96856 100644 --- a/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataImporter.java +++ b/artemis-cli/src/main/java/org/apache/activemq/artemis/cli/commands/tools/xml/XmlDataImporter.java @@ -321,7 +321,7 @@ private void sendMessage(List queues, Message message) throws Exception } for (String queue : queues) { - long queueID; + long queueID = -1; if (queueIDs.containsKey(queue)) { queueID = queueIDs.get(queue); @@ -337,17 +337,27 @@ private void sendMessage(List queues, Message message) throws Exception logger.debug("Requesting ID for: {}", queue); } ClientMessage reply = requestor.request(managementMessage); - Number idObject = (Number) ManagementHelper.getResult(reply); - queueID = idObject.longValue(); + if (ManagementHelper.hasOperationSucceeded(reply)) { + Number idObject = (Number) ManagementHelper.getResult(reply); + queueID = idObject.longValue(); + } else { + if (debugLog) { + logger.debug("Failed to get ID for {}, reply: {}", queue, ManagementHelper.getResult(reply, String.class)); + } + } } if (debugLog) { logger.debug("ID for {} is: {}", queue, queueID); } - queueIDs.put(queue, queueID); // store it so we don't have to look it up every time + if (queueID != -1) { + queueIDs.put(queue, queueID); // store it so we don't have to look it up every time + } } - buffer.putLong(queueID); + if (queueID != -1) { + buffer.putLong(queueID); + } if (debugLog) { debugLogMessage.append(queue).append(", "); } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java index 10e074142bc..48fb2b6b2a1 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/config/ActiveMQDefaultConfiguration.java @@ -692,6 +692,12 @@ public static String getDefaultHapolicyBackupStrategy() { 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 static long getDefaultEmbeddedWebServerRestartTimeout() { 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; + } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java index d7930c771bc..4d2cea380e4 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ActiveMQServerControl.java @@ -1406,6 +1406,21 @@ void addSecuritySettings(@Parameter(desc = "an address match", name = "addressMa @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; diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ObjectNameBuilder.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ObjectNameBuilder.java index d08bb27f446..2d103f59168 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ObjectNameBuilder.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/api/core/management/ObjectNameBuilder.java @@ -161,7 +161,12 @@ private String getActiveMQServerName() { 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"); } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/security/Role.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/security/Role.java index 315f66d2268..12f32588994 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/security/Role.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/core/security/Role.java @@ -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 Role(final String name, 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,9 +100,10 @@ public Role(final String name, 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, @@ -109,6 +115,22 @@ public Role(final String name, 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, + 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, + final boolean view, + final boolean edit) { if (name == null) { throw new NullPointerException("name is null"); } @@ -123,6 +145,8 @@ public Role(final String name, this.deleteNonDurableQueue = deleteNonDurableQueue; this.manage = manage; this.browse = browse; + this.view = view; + this.edit = edit; } public String getName() { @@ -247,7 +271,12 @@ public String toString() { 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 boolean equals(final Object o) { 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 int hashCode() { 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 void merge(Role other) { 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; } } diff --git a/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/SecurityFormatter.java b/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/SecurityFormatter.java index 14d2b20c5b7..9dfd54c3c56 100644 --- a/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/SecurityFormatter.java +++ b/artemis-core-client/src/main/java/org/apache/activemq/artemis/utils/SecurityFormatter.java @@ -61,7 +61,7 @@ public static Set createSecurity(String sendRoles, Set 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; } diff --git a/artemis-features/src/main/resources/org.apache.activemq.artemis.cfg b/artemis-features/src/main/resources/org.apache.activemq.artemis.cfg index 12bac35ba51..e756765a961 100644 --- a/artemis-features/src/main/resources/org.apache.activemq.artemis.cfg +++ b/artemis-features/src/main/resources/org.apache.activemq.artemis.cfg @@ -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 diff --git a/artemis-hawtio/artemis-console/pom.xml b/artemis-hawtio/artemis-console/pom.xml index 802c9296abe..e6632cee0d6 100644 --- a/artemis-hawtio/artemis-console/pom.xml +++ b/artemis-hawtio/artemis-console/pom.xml @@ -132,12 +132,19 @@ - ${project.build.directory}/${project.build.finalName}/index.html + + ${project.build.directory}/${project.build.finalName}/index.html + ${project.build.directory}/${project.build.finalName}/WEB-INF/web.xml + <title>.*</title> <title>${project.name}</title> + + <load-on-startup>1</load-on-startup> + <load-on-startup>-1</load-on-startup> + diff --git a/artemis-server-osgi/src/main/java/org/apache/activemq/artemis/osgi/OsgiBroker.java b/artemis-server-osgi/src/main/java/org/apache/activemq/artemis/osgi/OsgiBroker.java index 05b59846694..8e29aaee350 100644 --- a/artemis-server-osgi/src/main/java/org/apache/activemq/artemis/osgi/OsgiBroker.java +++ b/artemis-server-osgi/src/main/java/org/apache/activemq/artemis/osgi/OsgiBroker.java @@ -57,6 +57,7 @@ public class OsgiBroker { private String name; private String configurationUrl; private String rolePrincipalClass; + private String userPrincipalClass; private Map components; private Map> registrations; private ServiceTracker tracker; @@ -69,11 +70,16 @@ public void activate(ComponentContext cctx) throws Exception { 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"); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java index 2ed4ebca821..edb56c3bd76 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/Configuration.java @@ -1496,4 +1496,17 @@ default String resolvePropertiesSources(String propertiesFileUrl) { Configuration setLargeMessageSync(boolean largeMessageSync); boolean isLargeMessageSync(); + + String getViewPermissionMethodMatchPattern(); + + void setViewPermissionMethodMatchPattern(String permissionMatchPattern); + + boolean isManagementMessageRbac(); + + void setManagementMessageRbac(boolean val); + + String getManagementRbacPrefix(); + + void setManagementRbacPrefix(String prefix); + } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java index 4e7ecf87c14..fd65a73ef41 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/config/impl/ConfigurationImpl.java @@ -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 boolean isLargeMessageSync() { 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 { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java index 7808df5187b..09653e5d6d3 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/deployers/impl/FileConfigurationParser.java @@ -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 void parseMainConfig(final Element e, final Configuration config) throws 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 @@ protected Pair> parseSecurityRoles(final Node node, final Map< ArrayList browseRoles = new ArrayList<>(); ArrayList createAddressRoles = new ArrayList<>(); ArrayList deleteAddressRoles = new ArrayList<>(); + ArrayList viewRoles = new ArrayList<>(); + ArrayList editRoles = new ArrayList<>(); ArrayList allRoles = new ArrayList<>(); NodeList children = node.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { @@ -1187,6 +1199,10 @@ protected Pair> parseSecurityRoles(final Node node, final Map< 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 @@ protected Pair> parseSecurityRoles(final Node node, final Map< } 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; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java index 9169c91d994..2dcabb3b92b 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ActiveMQServerControlImpl.java @@ -2921,10 +2921,27 @@ public void addSecuritySettings(final String addressMatch, 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 void addSecuritySettings(final String addressMatch, 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 Object[] getRoles(final String addressMatch) throws Exception { 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 void clearAuthorizationCache() { } ((SecurityStoreImpl)server.getSecurityStore()).invalidateAuthorizationCache(); } + + public ActiveMQServer getServer() { + return server; + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/AddressControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/AddressControlImpl.java index 9044936d636..5aca9fa77f9 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/AddressControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/AddressControlImpl.java @@ -214,19 +214,7 @@ public Object[] getRoles() throws Exception { 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 String sendMessage(final Map headers, 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()); } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ManagementRemotingConnection.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ManagementRemotingConnection.java index 3c494356ed9..b3487a54d07 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ManagementRemotingConnection.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/management/impl/ManagementRemotingConnection.java @@ -35,6 +35,8 @@ public class ManagementRemotingConnection implements RemotingConnection { + Subject subject; + @Override public Object getID() { return "management"; @@ -170,11 +172,12 @@ public boolean isSupportsFlowControl() { @Override public void setSubject(Subject subject) { + this.subject = subject; } @Override public Subject getSubject() { - return null; + return subject; } @Override diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedSecuritySetting.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedSecuritySetting.java index d17de9ec4fa..02c4f45d962 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedSecuritySetting.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/persistence/config/PersistedSecuritySetting.java @@ -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 PersistedSecuritySetting() { * @param browseRoles * @param createAddressRoles * @param deleteAddressRoles + * @param viewRoles + * @param editRoles */ public PersistedSecuritySetting(final String addressMatch, final String sendRoles, @@ -77,7 +83,9 @@ public PersistedSecuritySetting(final String addressMatch, 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 PersistedSecuritySetting(final String addressMatch, 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 SimpleString getAddressMatch() { * @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 void encode(final ActiveMQBuffer buffer) { buffer.writeNullableSimpleString(browseRoles); buffer.writeNullableSimpleString(createAddressRoles); buffer.writeNullableSimpleString(deleteAddressRoles); + buffer.writeNullableSimpleString(viewRoles); + buffer.writeNullableSimpleString(editRoles); } @Override @@ -209,7 +234,9 @@ public int getEncodeSize() { (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 void decode(final ActiveMQBuffer buffer) { browseRoles = buffer.readNullableSimpleString(); createAddressRoles = buffer.readNullableSimpleString(); deleteAddressRoles = buffer.readNullableSimpleString(); + viewRoles = buffer.readNullableSimpleString(); + editRoles = buffer.readNullableSimpleString(); } /* (non-Javadoc) @@ -245,6 +274,8 @@ public int hashCode() { 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 boolean equals(Object obj) { 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 String toString() { createAddressRoles + ", deleteAddressRoles=" + deleteAddressRoles + + ", viewRoles=" + + viewRoles + + ", editRoles=" + editRoles + "]"; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/CheckType.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/CheckType.java index c4bd70539c0..ab345ac4b2c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/CheckType.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/CheckType.java @@ -76,7 +76,38 @@ public boolean hasRole(final Role role) { 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); } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java index 201e5213719..26440c379e6 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImpl.java @@ -46,7 +46,6 @@ 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 String authenticate(final String user, connection.setSubject(subject); } if (AuditLogger.isResourceLoggingEnabled()) { - AuditLogger.userSuccesfullyAuthenticatedInAudit(subject, connection.getRemoteAddress(), connection.getID().toString()); + if (connection != null) { + AuditLogger.userSuccesfullyAuthenticatedInAudit(subject, connection.getRemoteAddress(), connection.getID().toString()); + } else { + AuditLogger.userSuccesfullyAuthenticatedInAudit(subject, null, null); + } } return validatedUser; @@ -303,7 +306,7 @@ public void check(final SimpleString address, 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 void check(final SimpleString address, 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 set; String key = createAuthorizationCacheKey(user, checkType); ConcurrentHashSet act = getAuthorizationCacheEntry(key); @@ -340,19 +351,15 @@ public void onChange() { // we don't invalidate the authentication cache here because it's not necessary } - public static String getUserFromSubject(Subject subject) { - if (subject == null) { - return null; - } - - String validatedUser = ""; - Set users = subject.getPrincipals(UserPrincipal.class); + public String getUserFromSubject(Subject subject) { + return securityManager.getUserFromSubject(subject); + } - // 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 @@ private void authenticationFailed(String user, RemotingConnection connection) th 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; diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LegacyLDAPSecuritySettingPlugin.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LegacyLDAPSecuritySettingPlugin.java index d31a2429709..e9942c48ec7 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LegacyLDAPSecuritySettingPlugin.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/LegacyLDAPSecuritySettingPlugin.java @@ -463,7 +463,9 @@ private void processSearchResult(Map> securityRoles, 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 { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java index 3e3f268d64f..60d3079de46 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/impl/ServerSessionImpl.java @@ -2214,7 +2214,7 @@ private RoutingStatus handleManagementMessage(final Transaction tx, throw e; } - Message reply = managementService.handleMessage(message); + Message reply = managementService.handleMessage(this, message); SimpleString replyTo = message.getReplyTo(); diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerBuilder.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerBuilder.java index 58fc5cf31db..7eea8ed3143 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerBuilder.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerBuilder.java @@ -51,7 +51,7 @@ public MBeanServer newMBeanServer(String defaultDomain, MBeanServer outer, MBean private static final class MBeanInvocationHandler implements InvocationHandler { private final MBeanServer wrapped; - private final List guarded = Collections.unmodifiableList(Arrays.asList("invoke", "getAttribute", "getAttributes", "setAttribute", "setAttributes")); + private final List guarded = Collections.unmodifiableList(Arrays.asList("invoke", "getAttribute", "getAttributes", "setAttribute", "setAttributes", "queryMBeans")); MBeanInvocationHandler(MBeanServer mbeanServer) { wrapped = mbeanServer; @@ -75,9 +75,6 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl } 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) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerGuard.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerGuard.java index 735cb352103..b7f973ccde5 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerGuard.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisMBeanServerGuard.java @@ -32,14 +32,13 @@ 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 @@ private boolean canBypassRBAC(ObjectName objectName) { return jmxAccessControlList.isInAllowList(objectName); } + @Override public boolean canInvoke(String object, String operationName) { ObjectName objectName = null; try { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacInvocationHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacInvocationHandler.java new file mode 100644 index 00000000000..b4004336636 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacInvocationHandler.java @@ -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 + *

+ * 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.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 mBeanServerCheckedMethods = List.of("invoke", "getAttribute", "getAttributes", "setAttribute", "setAttributes", "queryMBeans", "queryNames"); + private final List 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.<.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; + } + }; +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacMBeanServerBuilder.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacMBeanServerBuilder.java new file mode 100644 index 00000000000..a4f6e599b61 --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacMBeanServerBuilder.java @@ -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); + } +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/GuardInvocationHandler.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/GuardInvocationHandler.java new file mode 100644 index 00000000000..5773a06bb6d --- /dev/null +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/GuardInvocationHandler.java @@ -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); +} diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java index 5109641838f..4586d6994e0 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementContext.java @@ -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 void setJmxConnectorConfiguration(JMXConnectorConfiguration jmxConnectorC 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) { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementService.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementService.java index 48dcf86c853..4fb88c1858a 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementService.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/ManagementService.java @@ -37,6 +37,7 @@ 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 @@ void registerBroadcastGroup(BroadcastGroup broadcastGroup, 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; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImpl.java index 1bce41a1ded..2a524676afe 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImpl.java @@ -19,7 +19,7 @@ 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 * */ 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; } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImpl.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImpl.java index 84b602a4199..bd514e61f60 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImpl.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImpl.java @@ -21,6 +21,7 @@ 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.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.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.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.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.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.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.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 ManagementServiceImpl(final MBeanServer mbeanServer, final Configuration broadcaster = new NotificationBroadcasterSupport(); notificationsEnabled = true; objectNameBuilder = ObjectNameBuilder.create(configuration.getJMXDomain(), configuration.getName(), configuration.isJMXUseBrokerName()); + managementMessageRbacResourceNamePrefix = configuration.isManagementMessageRbac() ? SimpleString.toSimpleString(configuration.getManagementRbacPrefix()).concat('.') : null; + viewPermissionMatcher = Pattern.compile(configuration.getViewPermissionMethodMatchPattern()); } @@ -448,11 +457,8 @@ public synchronized void registerBroadcastGroup(final BroadcastGroup broadcastGr } else { control = new BaseBroadcastGroupControlImpl(broadcastGroup, storageManager, configuration); } - //shouldnt ever be null - if (control != null) { - registerInJMX(objectName, control); - registerInRegistry(ResourceNames.BROADCAST_GROUP + configuration.getName(), control); - } + registerInJMX(objectName, control); + registerInRegistry(ResourceNames.BROADCAST_GROUP + configuration.getName(), control); } @Override @@ -512,22 +518,22 @@ public synchronized void unregisterConnectionRouter(final String name) throws Ex } @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 ICoreMessage handleMessage(Message message) throws Exception { } try { - Object result = invokeOperation(resourceName, operation, params); + Object result = invokeOperation(resourceName, operation, params, auth); ManagementHelper.storeResult(reply, result); @@ -574,7 +580,7 @@ public ICoreMessage handleMessage(Message message) throws Exception { if (attribute != null) { try { - Object result = getAttribute(resourceName, attribute); + Object result = getAttribute(resourceName, attribute, auth); ManagementHelper.storeResult(reply, result); @@ -596,6 +602,21 @@ public ICoreMessage handleMessage(Message message) throws Exception { 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 synchronized void stop() throws Exception { storageManager = null; - messagingServer = null; - registeredNames.clear(); started = false; @@ -849,7 +868,7 @@ public void enableNotifications(final boolean enabled) { } @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 Object getAttribute(final String resourceName, final String attribute) { throw ActiveMQMessageBundle.BUNDLE.noGetterMethod(attribute); } } + + final String methodName = method.getName(); + + securityCheck(resourceName + "." + methodName, permissionForInvoke(methodName), auth); + return method.invoke(resource, new Object[0]); } catch (Throwable t) { throw new IllegalStateException("Problem while retrieving attribute " + attribute, t); @@ -877,14 +901,16 @@ public Object getAttribute(final String resourceName, final String attribute) { @Override public Object invokeOperation(final String resourceName, - final String operation, - final Object[] params) throws Exception { + final String operation, + final Object[] params, + SecurityAuth auth) throws Exception { Object resource = registry.get(resourceName); if (resource == null) { throw ActiveMQMessageBundle.BUNDLE.cannotFindResource(resourceName); } + Method method = null; Method[] methods = resource.getClass().getMethods(); @@ -926,8 +952,10 @@ public Object invokeOperation(final String resourceName, 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); } /** diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/routing/targets/LocalTarget.java b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/routing/targets/LocalTarget.java index afa13cc95c9..cc0ffa4286c 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/routing/targets/LocalTarget.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/core/server/routing/targets/LocalTarget.java @@ -60,11 +60,11 @@ public boolean checkReadiness() { @Override public T getAttribute(String resourceName, String attributeName, Class attributeClass, int timeout) throws Exception { - return (T)managementService.getAttribute(resourceName, attributeName); + return (T)managementService.getAttribute(resourceName, attributeName, null); } @Override public T invokeOperation(String resourceName, String operationName, Object[] operationParams, Class operationClass, int timeout) throws Exception { - return (T)managementService.invokeOperation(resourceName, operationName, operationParams); + return (T)managementService.invokeOperation(resourceName, operationName, operationParams, null); } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQBasicSecurityManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQBasicSecurityManager.java index 09cfb841d15..101bf66572e 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQBasicSecurityManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQBasicSecurityManager.java @@ -56,7 +56,6 @@ public class ActiveMQBasicSecurityManager implements ActiveMQSecurityManager5, U public static final String BOOTSTRAP_ROLE_FILE = "bootstrapRoleFile"; private Map properties; - private String rolePrincipalClass = RolePrincipal.class.getName(); private StorageManager storageManager; @Override @@ -85,7 +84,7 @@ public Subject authenticate(final String userToAuthenticate, final String passwo 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 boolean authorize(final Subject subject, final Set 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 { diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java index e73ebaa6afb..db8b6067730 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManager.java @@ -20,6 +20,7 @@ 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.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 rolePrincipalClass = RolePrincipal.class; + private Class userPrincipalClass = UserPrincipal.class; public ActiveMQJAASSecurityManager() { } @@ -122,6 +125,11 @@ public boolean authorize(final Subject subject, 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 SecurityConfiguration getCertificateConfiguration() { } public String getRolePrincipalClass() { - return rolePrincipalClass; + return rolePrincipalClass.getName(); + } + + @SuppressWarnings("unchecked") + public void setRolePrincipalClass(String principalClass) throws ClassNotFoundException { + this.rolePrincipalClass = (Class) Class.forName(principalClass); + } + + public String getUserPrincipalClass() { + return userPrincipalClass.getName(); } - public void setRolePrincipalClass(String rolePrincipalClass) { - this.rolePrincipalClass = rolePrincipalClass; + @SuppressWarnings("unchecked") + public void setUserPrincipalClass(String principalClass) throws ClassNotFoundException { + this.userPrincipalClass = (Class) Class.forName(principalClass); } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager.java index afde137dfd3..40969679bf2 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager.java @@ -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 @@ default String getDomain() { default ActiveMQSecurityManager init(Map properties) { return this; } + + default String getUserFromSubject(Subject subject) { + return SecurityManagerUtil.getUserFromSubject(subject, UserPrincipal.class); + } } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager5.java b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager5.java index 2af3459e2d4..af8c8c82340 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager5.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/spi/core/security/ActiveMQSecurityManager5.java @@ -60,4 +60,5 @@ public interface ActiveMQSecurityManager5 extends ActiveMQSecurityManager { * @return true if the user is authorized, else false */ boolean authorize(Subject subject, Set roles, CheckType checkType, String address); + } diff --git a/artemis-server/src/main/java/org/apache/activemq/artemis/utils/SecurityManagerUtil.java b/artemis-server/src/main/java/org/apache/activemq/artemis/utils/SecurityManagerUtil.java index d3fb9efd036..b0bca64b492 100644 --- a/artemis-server/src/main/java/org/apache/activemq/artemis/utils/SecurityManagerUtil.java +++ b/artemis-server/src/main/java/org/apache/activemq/artemis/utils/SecurityManagerUtil.java @@ -33,7 +33,7 @@ public class SecurityManagerUtil { private static final String WILDCARD = "*"; - public static Set getPrincipalsInRole(final CheckType checkType, final Set roles, final String rolePrincipalClass) { + public static Set getPrincipalsInRole(final CheckType checkType, final Set roles, final Class rolePrincipalClass) { Set principals = new HashSet<>(); for (Role role : roles) { if (checkType.hasRole(role)) { @@ -47,7 +47,16 @@ public static Set getPrincipalsInRole(final CheckType checkType, return principals; } - public static Object createGroupPrincipal(String name, String groupClass) throws Exception { + public static String getUserFromSubject(Subject subject, Class 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 int hashCode() { } Object[] param = new Object[]{name}; - Class cls = Class.forName(groupClass); - Constructor[] constructors = cls.getConstructors(); int i; Object instance; @@ -83,7 +90,7 @@ public int hashCode() { 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 int hashCode() { /** * 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 roles, final CheckType checkType, final String rolePrincipalClass) { + public static boolean authorize(final Subject subject, final Set roles, final CheckType checkType, final Class rolePrincipalClass) { boolean authorized = false; if (subject != null) { @@ -115,7 +122,7 @@ public static boolean authorize(final Subject subject, final Set roles, fi // Check the caller's roles Set 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); } diff --git a/artemis-server/src/main/resources/schema/artemis-configuration.xsd b/artemis-server/src/main/resources/schema/artemis-configuration.xsd index 7bca911b289..d3779266bd2 100644 --- a/artemis-server/src/main/resources/schema/artemis-configuration.xsd +++ b/artemis-server/src/main/resources/schema/artemis-configuration.xsd @@ -935,6 +935,33 @@ + + + + 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. + + + + + + + + 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. + + + + + + + + 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. + + + + diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java index a1d9a7ee535..a0f74aa992c 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationParserTest.java @@ -461,6 +461,42 @@ public void testLiteralMatchMarkers() throws Exception { Assert.assertEquals("()", configuration.getLiteralMatchMarkers()); } + @Test + public void testViewPermissionMethodMatchPattern() throws Exception { + final String pattern = "^(get|list).+$"; + String configStr = "" + pattern + "\n\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 = "" + pattern + "\n\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 = "" + enabled + "\n\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 { diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java index 60a6a0e2d96..2b1da86de2f 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/config/impl/FileConfigurationTest.java @@ -700,35 +700,35 @@ public void testSecurityRoleMapping() throws Exception { Set 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)); diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/RoleTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/RoleTest.java index 46d038729dc..dca133419b7 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/RoleTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/RoleTest.java @@ -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 void testWriteRole() throws Exception { @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 void testReadRole() throws Exception { @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 void testCreateRole() throws Exception { @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 void testManageRole() throws Exception { @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 void testEqualsAndHashcode() throws Exception { 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)); } diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImplTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImplTest.java index ae22d1b8129..da3d23bf28f 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImplTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/impl/SecurityStoreImplTest.java @@ -25,7 +25,8 @@ import org.apache.activemq.artemis.core.settings.impl.HierarchicalObjectRepository; import org.apache.activemq.artemis.spi.core.protocol.RemotingConnection; import org.apache.activemq.artemis.spi.core.security.ActiveMQSecurityManager5; -import org.apache.activemq.artemis.spi.core.security.jaas.NoCacheLoginException; +import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal; +import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal; import org.apache.activemq.artemis.utils.RandomUtil; import org.junit.Test; @@ -34,35 +35,39 @@ public class SecurityStoreImplTest { - @Test - public void zeroCacheSizeTest() throws Exception { - ActiveMQSecurityManager5 securityManager = new ActiveMQSecurityManager5() { - @Override - public Subject authenticate(String user, - String password, - RemotingConnection remotingConnection, - String securityDomain) throws NoCacheLoginException { - return new Subject(); - } + final ActiveMQSecurityManager5 securityManager = new ActiveMQSecurityManager5() { + @Override + public Subject authenticate(String user, + String password, + RemotingConnection remotingConnection, + String securityDomain) { + Subject subject = new Subject(); + subject.getPrincipals().add(new UserPrincipal(user)); + return subject; + } - @Override - public boolean authorize(Subject subject, Set roles, CheckType checkType, String address) { - return true; - } + @Override + public boolean authorize(Subject subject, Set roles, CheckType checkType, String address) { + return true; + } - @Override - public boolean validateUser(String user, String password) { - return false; - } + @Override + public boolean validateUser(String user, String password) { + return false; + } - @Override - public boolean validateUserAndRole(String user, String password, Set roles, CheckType checkType) { - return false; - } - }; + @Override + public boolean validateUserAndRole(String user, String password, Set roles, CheckType checkType) { + 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 String getSecurityDomain() { assertEquals(0, securityStore.getAuthorizationCacheSize()); securityStore.invalidateAuthorizationCache(); // ensure this doesn't throw an NPE } -} \ No newline at end of file + + @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)); + } + +} diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java index 0482d1d37bb..3500e4c4ef6 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/security/jaas/JAASSecurityManagerClassLoadingTest.java @@ -30,7 +30,6 @@ 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 void testLoginClassloading() throws Exception { 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 roles = new HashSet<>(); roles.add(role); boolean authorizationResult = securityManager.authorize(result, roles, CheckType.SEND, "someaddress"); diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/group/impl/ClusteredResetMockTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/group/impl/ClusteredResetMockTest.java index 24539d39268..15483a69ee9 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/group/impl/ClusteredResetMockTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/group/impl/ClusteredResetMockTest.java @@ -40,17 +40,18 @@ 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 Object[] getResources(Class resourceType) { } @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 void unregisterHawtioSecurity() throws Exception { } @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; } diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacMBeanServerBuilderTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacMBeanServerBuilderTest.java new file mode 100644 index 00000000000..2a50faaa243 --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/ArtemisRbacMBeanServerBuilderTest.java @@ -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 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) () -> { + 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 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) () -> { + 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) () -> { + 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 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) () -> { + 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) () -> { + 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 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) () -> { + 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 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) () -> { + 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) () -> { + 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 viewRoles = new HashSet<>(); + viewRoles.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false)); + Set 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) () -> { + 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) () -> { + 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 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 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) () -> { + 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 viewerRole = new HashSet<>(); + viewerRole.add(new Role("viewers", false, false, false, false, false, false, false, false, false, false, true, false)); + + Set 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) () -> { + 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) () -> { + 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 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) () -> { + 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) () -> { + try { + return securityControl.canInvoke(runtimeName.toString(), "getVmName"); + } catch (Exception e1) { + return e1.getCause(); + } + }); + assertNotNull(result); + assertTrue((Boolean) result); + + result = Subject.doAs(viewSubject, (PrivilegedAction) () -> { + try { + return securityControl.canInvoke(runtimeName.toString(), "getVmName", new String[]{"args", "are", "ignored"}); + } catch (Exception e1) { + return e1.getCause(); + } + }); + assertNotNull(result); + assertTrue((Boolean) result); + + + Map> bulkQuery = new HashMap<>(); + bulkQuery.put(runtimeName.toString(), List.of("getVmName()", "getVersion()")); + + result = Subject.doAs(viewSubject, (PrivilegedAction) () -> { + 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")); + } +} diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImplSecurityStoreRbacTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImplSecurityStoreRbacTest.java new file mode 100644 index 00000000000..c2b5ab89587 --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImplSecurityStoreRbacTest.java @@ -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); + } +} diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImplTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImplTest.java index feff8484532..bff7fc2cf4b 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImplTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/HawtioSecurityControlImplTest.java @@ -26,6 +26,8 @@ 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 @@ 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 void testCanInvokeMBean() throws Exception { @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 void testCanInvokeMBean2() throws Exception { @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 void testCanInvokeMBeanNoGuard() throws Exception { @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 void testCanInvokeMethod() throws Exception { @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 void testCanInvokeMethodNoGuard() throws Exception { @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 void testCanInvokeBulk() throws Exception { @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); diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImplTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImplTest.java new file mode 100644 index 00000000000..dc951506c1c --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/server/management/impl/ManagementServiceImplTest.java @@ -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); + } +} \ No newline at end of file diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/core/settings/RepositoryTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/core/settings/RepositoryTest.java index 1e41ab9fe12..c0d51bd6ce1 100644 --- a/artemis-server/src/test/java/org/apache/activemq/artemis/core/settings/RepositoryTest.java +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/core/settings/RepositoryTest.java @@ -181,13 +181,13 @@ public void testSingleMatch() { public void testSingletwo() { securityRepository.addMatch("queues.another.aq.*", new HashSet()); HashSet 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 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 hashSet = securityRepository.getMatch("queues.another.andanother"); @@ -198,8 +198,8 @@ public void testSingletwo() { public void testWithoutWildcard() { securityRepository.addMatch("queues.1.*", new HashSet()); HashSet 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 hashSet = securityRepository.getMatch("queues.2.aq"); Assert.assertEquals(hashSet.size(), 2); diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManagerTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManagerTest.java new file mode 100644 index 00000000000..99d96a548bb --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/spi/core/security/ActiveMQJAASSecurityManagerTest.java @@ -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)); + } + +} \ No newline at end of file diff --git a/artemis-server/src/test/java/org/apache/activemq/artemis/utils/SecurityManagerUtilTest.java b/artemis-server/src/test/java/org/apache/activemq/artemis/utils/SecurityManagerUtilTest.java new file mode 100644 index 00000000000..97d93221ee1 --- /dev/null +++ b/artemis-server/src/test/java/org/apache/activemq/artemis/utils/SecurityManagerUtilTest.java @@ -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)); + } +} \ No newline at end of file diff --git a/artemis-unit-test-support/src/main/java/org/apache/activemq/artemis/utils/SubjectDotDoAsRule.java b/artemis-unit-test-support/src/main/java/org/apache/activemq/artemis/utils/SubjectDotDoAsRule.java new file mode 100644 index 00000000000..bdcde17e7b8 --- /dev/null +++ b/artemis-unit-test-support/src/main/java/org/apache/activemq/artemis/utils/SubjectDotDoAsRule.java @@ -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 + *

+ * 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.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) () -> { + try { + base.evaluate(); + } catch (Throwable e1) { + return new Exception(e1); + } + return null; + }); + if (e != null) { + throw e; + } + } + }; + } +} diff --git a/docs/user-manual/configuration-index.adoc b/docs/user-manual/configuration-index.adoc index 16d8c92b759..d46bdc54485 100644 --- a/docs/user-manual/configuration-index.adoc +++ b/docs/user-manual/configuration-index.adoc @@ -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 diff --git a/docs/user-manual/management.adoc b/docs/user-manual/management.adoc index f71a95b0c24..2b6568f1dbb 100644 --- a/docs/user-manual/management.adoc +++ b/docs/user-manual/management.adoc @@ -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] +---- + + + + +---- + +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] +---- + + + +---- + + +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] +---- + + + +---- + + ==== 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: ---- -=== 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. + + ... + +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] +---- + + + + +---- + +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] +---- + + + + + +---- + +=== Management Example See the xref:examples.adoc#management[Management Example] which shows how to use JMS messages to manage the Apache ActiveMQ Artemis server. diff --git a/docs/user-manual/security.adoc b/docs/user-manual/security.adoc index f090d92b990..ebf34f1c6f5 100644 --- a/docs/user-manual/security.adoc +++ b/docs/user-manual/security.adoc @@ -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` ---- -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.: diff --git a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/JMSSaslExternalLDAPTest.java b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/JMSSaslExternalLDAPTest.java index 99acf6faf51..2138305e9b7 100644 --- a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/JMSSaslExternalLDAPTest.java +++ b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/JMSSaslExternalLDAPTest.java @@ -133,7 +133,7 @@ public void startServer() throws Exception { // 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("TEST", roles); diff --git a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/SaslKrb5LDAPSecurityTest.java b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/SaslKrb5LDAPSecurityTest.java index d7fea9fbc7d..5aeb4d469f4 100644 --- a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/SaslKrb5LDAPSecurityTest.java +++ b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/amqp/SaslKrb5LDAPSecurityTest.java @@ -400,7 +400,7 @@ public void dotestJAASSecurityManagerAuthorizationPositive(String jaasConfigScop createArtemisServer(jaasConfigScope); Set 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(); diff --git a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/JmxSecurityMultipleSubjectTest.java b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/JmxSecurityMultipleSubjectTest.java new file mode 100644 index 00000000000..021af204366 --- /dev/null +++ b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/JmxSecurityMultipleSubjectTest.java @@ -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 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) () -> { + 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) () -> { + 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) () -> { + try { + activeMQServerControl.forceFailover(); + return null; + } catch (Exception e1) { + return e1; + } + }); + assertNotNull("no permission is sufficient", e); + + } +} \ No newline at end of file diff --git a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/JmxSecurityTest.java b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/JmxSecurityTest.java new file mode 100644 index 00000000000..67a7d2e1b35 --- /dev/null +++ b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/JmxSecurityTest.java @@ -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 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 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 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 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 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 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 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 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 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() { + @Override + public Long run() throws Exception { + return queueControlA.countMessages(); + } + })); + + // client B view queue A + try { + assertEquals(Long.valueOf(0), Subject.doAs(subjectB, new PrivilegedExceptionAction() { + @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() { + @Override + public Long run() throws Exception { + return queueControlB.countMessages(); + } + })); + + + // client A View queue B + try { + assertEquals(Long.valueOf(0), Subject.doAs(subjectA, new PrivilegedExceptionAction() { + @Override + public Long run() throws Exception { + return queueControlB.countMessages(); + } + })); + Assert.fail("should throw exception here"); + } catch (Exception e) { + assertTrue(e instanceof SecurityException); + } + } + +} diff --git a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/LDAPSecurityTest.java b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/LDAPSecurityTest.java index face4455de4..c76ceaa0f52 100644 --- a/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/LDAPSecurityTest.java +++ b/tests/integration-tests-isolated/src/test/java/org/apache/activemq/artemis/tests/integration/isolated/security/LDAPSecurityTest.java @@ -175,7 +175,7 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { final SimpleString NON_DURABLE_QUEUE = new SimpleString("nonDurableQueue"); Set 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 void testJAASSecurityManagerAuthorizationPositive() throws Exception { final SimpleString NON_DURABLE_QUEUE = new SimpleString("nonDurableQueue"); Set 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(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/MultiThreadedAuditLoggingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/MultiThreadedAuditLoggingTest.java index 49c497f3ab3..6ba3312a303 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/MultiThreadedAuditLoggingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/MultiThreadedAuditLoggingTest.java @@ -59,10 +59,10 @@ public void setUp() throws Exception { server.setSecurityManager(new ActiveMQBasicSecurityManager()); server.start(); Set 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); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpClientTestSupport.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpClientTestSupport.java index 7eb4c5221a6..e562203df9b 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpClientTestSupport.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/AmqpClientTestSupport.java @@ -218,10 +218,10 @@ protected void enableSecurity(ActiveMQServer server, String... securityMatches) // Configure roles HierarchicalRepository> securityRepository = server.getSecurityRepository(); HashSet 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) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslExternalTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslExternalTest.java index 078d39f8af9..ddcf0b3e4cd 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslExternalTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslExternalTest.java @@ -114,7 +114,7 @@ public void startServer() throws Exception { // 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("TEST", roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslGssapiTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslGssapiTest.java index 1408a8cde3c..888f3b98996 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslGssapiTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/amqp/JMSSaslGssapiTest.java @@ -161,7 +161,7 @@ protected void configureBrokerSecurity(ActiveMQServer server) { 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(getQueueName().toString(), roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/AutoCreateJmsDestinationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/AutoCreateJmsDestinationTest.java index 8016ff57b40..fe659c8c9e4 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/AutoCreateJmsDestinationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/AutoCreateJmsDestinationTest.java @@ -207,7 +207,7 @@ public void testAutoCreateOnSendToQueueSecurity() throws Exception { ((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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("#", roles); @@ -481,7 +481,7 @@ public void setUp() throws Exception { ((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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("#", roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/CoreClientTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/CoreClientTest.java index 4d31c7a0f17..2aaaa0bd9d6 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/CoreClientTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/client/CoreClientTest.java @@ -214,7 +214,7 @@ public void internalTestCoreClientPrefixes(boolean security) throws Exception { 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 anySet = new HashSet<>(); anySet.add(myRole); server.getSecurityRepository().addMatch(baseAddress, anySet); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SecurityFailoverTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SecurityFailoverTest.java index c73689c7b57..a87789a3c71 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SecurityFailoverTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/cluster/failover/SecurityFailoverTest.java @@ -103,7 +103,7 @@ protected void beforeRestart(TestableServer primaryServer1) { 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 roles = new HashSet<>(); roles.add(role); server.getServer().getSecurityRepository().addMatch("#", roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java index 7f3d7493f4b..d0b83796354 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/RedeployTest.java @@ -48,6 +48,7 @@ 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 void testRedeploySecuritySettings() throws Exception { 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 void testRedeploySecuritySettingsWithManagementChange() throws Exception 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 void testChangeDivertAddress() throws Exception { @Test public void testRedeployWithFailover() throws Exception { Set 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 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)); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java index 8399f57a44f..f58ca935a67 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/SimpleJNDIClientTest.java @@ -575,7 +575,7 @@ public void testRemoteCFWithTCPUserPassword() throws Exception { //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 consumerCreateRoles = new HashSet<>(); consumerCreateRoles.add(consumeCreateRole); liveService.getSecurityRepository().addMatch("test.queue", consumerCreateRoles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/TemporaryDestinationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/TemporaryDestinationTest.java index 463ad297433..0774fa5d70a 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/TemporaryDestinationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/client/TemporaryDestinationTest.java @@ -329,7 +329,7 @@ public void testForSecurityCacheLeak() throws Exception { 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 anySet = new HashSet<>(); anySet.add(myRole); server.getSecurityRepository().addMatch("#", anySet); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/multiprotocol/MultiprotocolJMSClientTestSupport.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/multiprotocol/MultiprotocolJMSClientTestSupport.java index 8088d67e8d4..e4652a31ab5 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/multiprotocol/MultiprotocolJMSClientTestSupport.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/jms/multiprotocol/MultiprotocolJMSClientTestSupport.java @@ -238,10 +238,10 @@ protected void enableSecurity(ActiveMQServer server, String... securityMatches) // Configure roles HierarchicalRepository> securityRepository = server.getSecurityRepository(); HashSet 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) { diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/largemessage/ServerLargeMessageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/largemessage/ServerLargeMessageTest.java index 0c58db73e76..75ea047802d 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/largemessage/ServerLargeMessageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/largemessage/ServerLargeMessageTest.java @@ -153,7 +153,7 @@ public void testSendServerMessageWithValidatedUser() throws Exception { 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("#", roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java index 5c7d34c7a34..a30cc03a32c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlTest.java @@ -238,7 +238,7 @@ public void testSecurityCacheSizes() throws Exception { 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 void testSecurityCacheSizes() throws Exception { 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 void testClearingSecurityCaches() throws Exception { 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 void testSecuritySettings() throws Exception { 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 void setUp() throws Exception { server.start(); HashSet 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); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java index 9ef4b6755fb..44c72ed32f2 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ActiveMQServerControlUsingCoreTest.java @@ -938,6 +938,22 @@ public void addSecuritySettings(String addressMatch, 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 void addSecuritySettings(String addressMatch, 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 diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/AddressControlTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/AddressControlTest.java index 576d5499ba5..7ef3cbf1a51 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/AddressControlTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/AddressControlTest.java @@ -224,7 +224,7 @@ public void testGetBindingNames() throws Exception { 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 void testGetRoles() throws Exception { 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)); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementServiceImplTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementServiceImplTest.java index 83a284cfa1c..b721342053d 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementServiceImplTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/ManagementServiceImplTest.java @@ -57,7 +57,7 @@ public void testHandleManagementMessageWithOperation() throws Exception { 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 void testHandleManagementMessageWithOperationWhichFails() throws Exceptio 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 void testHandleManagementMessageWithUnknowResource() throws Exception { 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 void testHandleManagementMessageWithUnknownAttribute() throws Exception { 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 void testHandleManagementMessageWithKnownAttribute() throws Exception { 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 void testCorrelateResponseByCorrelationID() throws Exception { 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 void testCorrelateResponseByMessageID() throws Exception { 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)); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/MessageAuthorizationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/MessageAuthorizationTest.java index f06ac8f771b..b4cd563eb34 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/MessageAuthorizationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/MessageAuthorizationTest.java @@ -68,9 +68,9 @@ public void setUp() throws Exception { server = addServer(ActiveMQServers.newActiveMQServer(createDefaultNettyConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, true)); server.getConfiguration().setPopulateValidatedUser(true); Set 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(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SSLSecurityNotificationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SSLSecurityNotificationTest.java index 6987ad4a370..23f7f9e75ae 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SSLSecurityNotificationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SSLSecurityNotificationTest.java @@ -114,7 +114,7 @@ public void testCONSUMER_CREATED() throws Exception { 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 roles = new HashSet<>(); roles.add(role); @@ -153,7 +153,7 @@ public void testCONSUMER_CREATED() throws Exception { @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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("#", roles); @@ -208,7 +208,7 @@ public void setUp() throws Exception { 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(ActiveMQDefaultConfiguration.getDefaultManagementNotificationAddress().toString(), roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementMessageRbacTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementMessageRbacTest.java new file mode 100644 index 00000000000..23ea30a089a --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementMessageRbacTest.java @@ -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> 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 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 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 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; + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementTestBase.java index 499aa68a474..1cac15ef194 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementTestBase.java @@ -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.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 getParams() { + return Arrays.asList(new Object[][]{{true}, {false}}); + } @Override @@ -48,9 +59,31 @@ public void setUp() throws Exception { protected abstract ActiveMQServer setupAndStartActiveMQServer() throws Exception; - protected void doSendManagementMessage(final String user, - final String password, - final boolean expectSuccess) throws Exception { + @Override + protected Configuration createDefaultInVMConfig() throws Exception { + Configuration configuration = super.createDefaultInVMConfig(); + if (managementRbac) { + configuration.setManagementMessageRbac(true); // enable rbac view/update perms check + } + return configuration; + } + + protected void doSendBrokerManagementMessage(final String user, + final String password, + final boolean expectSuccess) throws Exception { + doSendBrokerManagementMessageForAttribute(user, password, expectSuccess); + } + + protected void doSendBrokerManagementMessageForAttribute(final String user, + final String password, + final boolean expectSuccess) throws Exception { + doSendBrokerManagementMessageFor(true, user, password, expectSuccess); + } + + protected void doSendBrokerManagementMessageFor(final boolean attribute, final String user, + final String password, + final boolean expectSuccess) throws Exception { + ServerLocator locator = createInVMNonHALocator(); ClientSessionFactory sf = locator.createSessionFactory(); try { @@ -66,26 +99,40 @@ protected void doSendManagementMessage(final String user, ClientRequestor requestor = new ClientRequestor(session, ActiveMQDefaultConfiguration.getDefaultManagementAddress()); ClientMessage mngmntMessage = session.createMessage(false); - ManagementHelper.putAttribute(mngmntMessage, ResourceNames.BROKER, "started"); + if (attribute) { + ManagementHelper.putAttribute(mngmntMessage, ResourceNames.BROKER, "started"); + } else { + ManagementHelper.putOperationInvocation(mngmntMessage, ResourceNames.BROKER, "enableMessageCounters"); + } ClientMessage reply = requestor.request(mngmntMessage, 500); if (expectSuccess) { Assert.assertNotNull(reply); - Assert.assertTrue((Boolean) ManagementHelper.getResult(reply)); + Assert.assertTrue("" + ManagementHelper.getResult(reply), (Boolean) ManagementHelper.hasOperationSucceeded(reply)); + if (attribute) { + Assert.assertTrue("" + ManagementHelper.getResult(reply), (Boolean) ManagementHelper.getResult(reply)); + } } else { - Assert.assertNull(reply); + if (attribute) { + Assert.assertNull(reply); + } else { + Assert.assertNotNull(reply); + Assert.assertFalse("" + ManagementHelper.getResult(reply), (Boolean) ManagementHelper.hasOperationSucceeded(reply)); + } } requestor.close(); - } catch (Exception e) { + } catch (ActiveMQSecurityException possiblyExpected) { if (expectSuccess) { - Assert.fail("got unexpected exception " + e.getClass() + ": " + e.getMessage()); - e.printStackTrace(); + Assert.fail("got unexpected security exception " + possiblyExpected.getClass() + ": " + possiblyExpected.getMessage()); } + } catch (ActiveMQClusterSecurityException possiblyExpected) { + if (expectSuccess) { + Assert.fail("got unexpected security exception " + possiblyExpected.getClass() + ": " + possiblyExpected.getMessage()); + } + } catch (Exception e) { + Assert.fail("got unexpected exception " + e.getClass() + ": " + e.getMessage()); } finally { sf.close(); } } - - - } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithConfiguredAdminUserTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithConfiguredAdminUserTest.java index 83e748bf9f8..a5ee8954a94 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithConfiguredAdminUserTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithConfiguredAdminUserTest.java @@ -26,7 +26,10 @@ 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 @@ protected ActiveMQServer setupAndStartActiveMQServer() throws Exception { securityManager.getConfiguration().addRole(invalidAdminUser, "guest"); Set 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 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; diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithDefaultConfigurationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithDefaultConfigurationTest.java index 171072a4272..0b44a02aed4 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithDefaultConfigurationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithDefaultConfigurationTest.java @@ -21,22 +21,25 @@ 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); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithModifiedConfigurationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithModifiedConfigurationTest.java index 91c6f5b6445..06e65ff9278 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithModifiedConfigurationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityManagementWithModifiedConfigurationTest.java @@ -21,7 +21,10 @@ 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); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityNotificationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityNotificationTest.java index 1e9e55de629..c636d18a2f3 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityNotificationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/management/SecurityNotificationTest.java @@ -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.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 void testSECURITY_PERMISSION_VIOLATION() throws Exception { 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(address.toString(), roles); @@ -131,12 +138,54 @@ public void testSECURITY_PERMISSION_VIOLATION() throws Exception { 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() { + @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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(address.toString(), roles); @@ -173,7 +222,7 @@ public void testCONSUMER_CREATED() throws Exception { 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 void setUp() throws Exception { 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(ActiveMQDefaultConfiguration.getDefaultManagementNotificationAddress().toString(), roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/MQTTSecurityManagerTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/MQTTSecurityManagerTest.java index 802af061868..f8ed0ea4fca 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/MQTTSecurityManagerTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/MQTTSecurityManagerTest.java @@ -30,6 +30,7 @@ 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 Subject authenticate(String user, throw new InvalidClientIdException(); } remotingConnection.setClientID(clientID); - return new Subject(); + Subject subject = new Subject(); + subject.getPrincipals().add(new UserPrincipal(user)); + return subject; } @Override diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/MQTTTestSupport.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/MQTTTestSupport.java index a15360f115a..42655b81aaa 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/MQTTTestSupport.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/MQTTTestSupport.java @@ -198,10 +198,10 @@ protected void configureBrokerSecurity(ActiveMQServer server) { // Configure roles HierarchicalRepository> securityRepository = server.getSecurityRepository(); HashSet 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); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/PahoMQTTQOS2SecurityTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/PahoMQTTQOS2SecurityTest.java index 0af994e89c2..fe84ba1d971 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/PahoMQTTQOS2SecurityTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt/PahoMQTTQOS2SecurityTest.java @@ -52,7 +52,7 @@ protected void configureBrokerSecurity(ActiveMQServer server) { // Configure roles HierarchicalRepository> securityRepository = server.getSecurityRepository(); HashSet 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); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/MQTT5TestSupport.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/MQTT5TestSupport.java index e2cd5e66b97..42eb40002cd 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/MQTT5TestSupport.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/MQTT5TestSupport.java @@ -217,12 +217,12 @@ protected void configureBrokerSecurity(ActiveMQServer server) { // Configure roles HierarchicalRepository> securityRepository = server.getSecurityRepository(); HashSet 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); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/ssl/CertificateAuthenticationSslTests.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/ssl/CertificateAuthenticationSslTests.java index 2f06512d2d8..a09f041684b 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/ssl/CertificateAuthenticationSslTests.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/mqtt5/ssl/CertificateAuthenticationSslTests.java @@ -83,7 +83,7 @@ protected void configureBrokerSecurity(ActiveMQServer server) { server.setSecurityManager(new ActiveMQJAASSecurityManager("CertLogin")); server.getConfiguration().setSecurityEnabled(true); HashSet 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); } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/OpenWireTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/OpenWireTestBase.java index a8d2cdf6c81..086ac4bb12f 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/OpenWireTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/OpenWireTestBase.java @@ -73,23 +73,23 @@ public void setUp() throws Exception { 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 roles = new HashSet<>(); roles.add(senderRole); @@ -101,7 +101,7 @@ public void setUp() throws Exception { // 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); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SecurityOpenWireTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SecurityOpenWireTest.java index 9b4e2beb8ad..1e8bd859ee6 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SecurityOpenWireTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/openwire/SecurityOpenWireTest.java @@ -62,7 +62,7 @@ protected void extraServerConfig(Configuration serverConfig) { @Test public void testSendNoAuth() throws Exception { Set 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"); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/RolesConfigurationStorageTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/RolesConfigurationStorageTest.java index 8e4a86a7459..0653de88911 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/RolesConfigurationStorageTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/RolesConfigurationStorageTest.java @@ -50,9 +50,9 @@ protected void addSetting(PersistedSecuritySetting setting) throws Exception { 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 void testStoreSecuritySettings() throws Exception { 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 void testStoreSecuritySettings2() throws Exception { 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(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/XmlImportExportRbacTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/XmlImportExportRbacTest.java new file mode 100644 index 00000000000..63e171bd948 --- /dev/null +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/persistence/XmlImportExportRbacTest.java @@ -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 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 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)); + } + } +} diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ActiveMQMessageHandlerSecurityTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ActiveMQMessageHandlerSecurityTest.java index 143ec27a435..4f4c6c70ae0 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ActiveMQMessageHandlerSecurityTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/ActiveMQMessageHandlerSecurityTest.java @@ -67,7 +67,7 @@ public void testSimpleMessageReceivedOnQueueWithSecuritySucceeds() throws Except 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/JMSContextTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/JMSContextTest.java index 463fe863a43..8a1c9079a03 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/JMSContextTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/JMSContextTest.java @@ -57,7 +57,7 @@ public void setUp() throws Exception { 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionJTATest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionJTATest.java index 11f61f50b4e..957b3e187dc 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionJTATest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionJTATest.java @@ -71,7 +71,7 @@ public void setUp() throws Exception { ((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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionNoJTATest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionNoJTATest.java index 430c0f3fda9..c8dd4965bfa 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionNoJTATest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionNoJTATest.java @@ -71,7 +71,7 @@ public void setUp() throws Exception { ((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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionTest.java index feed83c97c7..0d80c01f89c 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ra/OutgoingConnectionTest.java @@ -82,7 +82,7 @@ public void setUp() throws Exception { 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch(MDBQUEUEPREFIXED, roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/routing/KeyTypeTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/routing/KeyTypeTest.java index 729b7d661c3..71020156473 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/routing/KeyTypeTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/routing/KeyTypeTest.java @@ -241,7 +241,7 @@ public void testRoleNameKeyLocalTarget() throws Exception { // ensure advisory permission is present for openwire connection creation by 'b' HierarchicalRepository> 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 roles = new HashSet<>(); roles.add(role); securityRepository.addMatch("ActiveMQ.Advisory.#", roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerTest.java index 6a15cebc405..3a1f495c92e 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/BasicSecurityManagerTest.java @@ -131,7 +131,7 @@ public void testWithValidatedUser() throws Exception { ActiveMQServer server = initializeServer(); server.getConfiguration().setPopulateValidatedUser(true); server.start(); - Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true); + Role role = new Role("programmers", true, true, true, true, true, true, true, true, true, true, false, false); Set roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("#", roles); @@ -178,7 +178,7 @@ public void testAuthorizationNegative() throws Exception { ActiveMQServer server = initializeServer(); Set 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 void testAuthorizationPositive() throws Exception { ActiveMQServer server = initializeServer(); Set 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(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/FQQNSendSecurityTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/FQQNSendSecurityTest.java index a1db3e9b316..5e512d3a720 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/FQQNSendSecurityTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/FQQNSendSecurityTest.java @@ -54,8 +54,8 @@ public void setUp() throws Exception { 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); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/PersistedSecuritySettingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/PersistedSecuritySettingTest.java index 1b60425bfe6..3d82b592cc1 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/PersistedSecuritySettingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/PersistedSecuritySettingTest.java @@ -40,5 +40,7 @@ public void testNPE() { persistedSecuritySetting.getDeleteNonDurableQueueRoles(); persistedSecuritySetting.getManageRoles(); persistedSecuritySetting.getSendRoles(); + persistedSecuritySetting.getViewRoles(); + persistedSecuritySetting.getEditRoles(); } } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/RecursiveNettySecurityTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/RecursiveNettySecurityTest.java index aa925e177fc..891a353f345 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/RecursiveNettySecurityTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/RecursiveNettySecurityTest.java @@ -34,6 +34,7 @@ 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.UserPrincipal; import org.apache.activemq.artemis.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.tests.util.CFUtil; import org.junit.Assert; @@ -105,7 +106,9 @@ public Subject authenticate(String user, logger.warn(e.getMessage(), e); throw new NoCacheLoginException(e.getMessage()); } - return new Subject(); + Subject authenticatedSubject = new Subject(); + authenticatedSubject.getPrincipals().add(new UserPrincipal(user)); + return authenticatedSubject; } @Override diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorJmsTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorJmsTest.java index 64c84c49450..7a2ab059fd9 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorJmsTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorJmsTest.java @@ -115,12 +115,12 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(); ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setResolveProtocols(true).addAcceptorConfiguration("netty", "tcp://127.0.0.1:61616?securityDomain=PropertiesLogin").setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set 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); // ensure advisory permission is still set for openwire to allow connection to succeed, alternative is url param jms.watchTopicAdvisories=false on the client connection factory roles = new HashSet<>(); - roles.add(new Role("programmers", false, true, false, false, true, true, false, false, true, false)); + roles.add(new Role("programmers", false, true, false, false, true, true, false, false, true, false, false, false)); server.getConfiguration().putSecurityRoles("ActiveMQ.Advisory.#", roles); server.start(); @@ -165,7 +165,7 @@ public void testJAASSecurityManagerAuthorizationPositive() throws Exception { ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true).setResolveProtocols(true).addAcceptorConfiguration("netty", "tcp://127.0.0.1:61616?securityDomain=PropertiesLogin"), ManagementFactory.getPlatformMBeanServer(), new ActiveMQJAASSecurityManager(), false)); Set 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(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorTest.java index 43a840af0b4..d7032b477ae 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityPerAcceptorTest.java @@ -108,7 +108,7 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(); ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().addAcceptorConfiguration("acceptor", acceptorUrl).setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set 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)); @@ -194,7 +194,7 @@ public void testJAASSecurityManagerAuthorizationPositive() throws Exception { ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager(); ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true).addAcceptorConfiguration("acceptor", acceptorUrl), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set 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(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java index 863005bf410..19fbec3dcb2 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/security/SecurityTest.java @@ -72,6 +72,7 @@ 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.tests.util.ActiveMQTestBase; import org.apache.activemq.artemis.utils.CompositeAddress; import org.apache.activemq.artemis.utils.SensitiveDataCodec; @@ -145,7 +146,9 @@ public Subject authenticate(String user, String securityDomain) throws NoCacheLoginException { flipper = !flipper; if (flipper) { - return new Subject(); + Subject validatedSubject = new Subject(); + validatedSubject.getPrincipals().add(new UserPrincipal(user)); + return validatedSubject; } else { throw new NoCacheLoginException(); } @@ -236,7 +239,7 @@ public void testJAASSecurityManagerAuthenticationWithValidateUser() throws Excep ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); 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 roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("#", roles); @@ -332,7 +335,7 @@ public void testJAASSecurityManagerAuthenticationWithCertsAndOpenWire() throws E // ensure advisory permission is still set for openwire to allow connection to succeed, alternative is url param jms.watchTopicAdvisories=false on the client connection factory HashSet roles = new HashSet<>(); - roles.add(new Role("programmers", false, true, false, false, true, true, false, false, true, false)); + roles.add(new Role("programmers", false, true, false, false, true, true, false, false, true, false, false, false)); server.getConfiguration().putSecurityRoles("ActiveMQ.Advisory.#", roles); server.start(); @@ -363,7 +366,7 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set 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); Map params = new HashMap<>(); @@ -378,7 +381,7 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { server.start(); ActiveMQSslConnectionFactory factory = new ActiveMQSslConnectionFactory("ssl://localhost:61616?verifyHostName=false"); - factory.setUserName("test-user"); + // factory.setUserName("test-user"); factory.setTrustStore("server-ca-truststore.jks"); factory.setTrustStorePassword("securepass"); factory.setKeyStore("client-keystore.jks"); @@ -399,7 +402,8 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { session.createConsumer(session.createQueue("test.queue")); Assert.fail("should throw exception here"); } catch (Exception e) { - assertTrue(e.getMessage().contains("User: test-user does not have permission='CREATE_DURABLE_QUEUE' for queue test.queue on address test.queue")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='CREATE_DURABLE_QUEUE' for queue test.queue on address test.queue")); } //Test non durable create permission @@ -407,7 +411,8 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { session.createConsumer(session.createTopic("test.topic")); Assert.fail("should throw exception here"); } catch (Exception e) { - assertTrue(e.getMessage().contains("User: test-user does not have permission='CREATE_NON_DURABLE_QUEUE'")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='CREATE_NON_DURABLE_QUEUE'")); } //Add a test queue to the server @@ -419,7 +424,8 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { producer.send(session.createMessage()); Assert.fail("should throw exception here"); } catch (Exception e) { - assertTrue(e.getMessage().contains("User: test-user does not have permission='SEND'")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='SEND'")); } //Test queue consume permission @@ -427,7 +433,8 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { session.createConsumer(session.createQueue("test.queue")); Assert.fail("should throw exception here"); } catch (Exception e) { - assertTrue(e.getMessage().contains("User: test-user does not have permission='CONSUME' for queue test.queue on address test.queue")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='CONSUME' for queue test.queue on address test.queue")); } //Test queue browse permission @@ -436,7 +443,8 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { browser.getEnumeration(); Assert.fail("should throw exception here"); } catch (Exception e) { - assertTrue(e.getMessage().contains("User: test-user does not have permission='BROWSE' for queue test.queue on address test.queue")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='BROWSE' for queue test.queue on address test.queue")); } //Test queue deletion permission @@ -444,7 +452,8 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { connection.destroyDestination(new ActiveMQQueue("test.queue")); Assert.fail("should throw exception here"); } catch (Exception e) { - assertTrue(e.getMessage().contains("User: test-user does not have permission='DELETE_DURABLE_QUEUE' for queue test.queue on address test.queue")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='DELETE_DURABLE_QUEUE' for queue test.queue on address test.queue")); } //Test temp queue @@ -452,7 +461,8 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { session.createTemporaryQueue(); Assert.fail("should throw exception here"); } catch (Exception e) { - assertTrue(e.getMessage().contains("User: test-user does not have permission='CREATE_ADDRESS'")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='CREATE_ADDRESS'")); } //Test temp topic @@ -460,7 +470,8 @@ public void testJAASSecurityManagerOpenWireNegative() throws Exception { session.createTemporaryTopic(); Assert.fail("should throw exception here"); } catch (Exception e) { - assertTrue(e.getMessage().contains("User: test-user does not have permission='CREATE_ADDRESS'")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='CREATE_ADDRESS'")); } session.close(); @@ -549,7 +560,7 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("PropertiesLogin"); ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set 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)); @@ -564,7 +575,8 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { session.createQueue(new QueueConfiguration(DURABLE_QUEUE).setAddress(ADDRESS)); Assert.fail("should throw exception here"); } catch (ActiveMQException e) { - assertTrue(e.getMessage().contains("User: first does not have permission='CREATE_DURABLE_QUEUE' for queue durableQueue on address address")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='CREATE_DURABLE_QUEUE' for queue durableQueue on address address")); } // DELETE_DURABLE_QUEUE @@ -572,7 +584,8 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { session.deleteQueue(DURABLE_QUEUE); Assert.fail("should throw exception here"); } catch (ActiveMQException e) { - assertTrue(e.getMessage().contains("User: first does not have permission='DELETE_DURABLE_QUEUE' for queue durableQueue on address address")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='DELETE_DURABLE_QUEUE' for queue durableQueue on address address")); } // CREATE_NON_DURABLE_QUEUE @@ -580,7 +593,8 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { session.createQueue(new QueueConfiguration(NON_DURABLE_QUEUE).setAddress(ADDRESS).setDurable(false)); Assert.fail("should throw exception here"); } catch (ActiveMQException e) { - assertTrue(e.getMessage().contains("User: first does not have permission='CREATE_NON_DURABLE_QUEUE' for queue nonDurableQueue on address address")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='CREATE_NON_DURABLE_QUEUE' for queue nonDurableQueue on address address")); } // DELETE_NON_DURABLE_QUEUE @@ -588,7 +602,8 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { session.deleteQueue(NON_DURABLE_QUEUE); Assert.fail("should throw exception here"); } catch (ActiveMQException e) { - assertTrue(e.getMessage().contains("User: first does not have permission='DELETE_NON_DURABLE_QUEUE' for queue nonDurableQueue on address address")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='DELETE_NON_DURABLE_QUEUE' for queue nonDurableQueue on address address")); } // PRODUCE @@ -597,7 +612,8 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { producer.send(session.createMessage(true)); Assert.fail("should throw exception here"); } catch (ActiveMQException e) { - assertTrue(e.getMessage().contains("User: first does not have permission='SEND' on address address")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='SEND' on address address")); } // CONSUME @@ -605,7 +621,8 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { ClientConsumer consumer = session.createConsumer(DURABLE_QUEUE); Assert.fail("should throw exception here"); } catch (ActiveMQException e) { - assertTrue(e.getMessage().contains("User: first does not have permission='CONSUME' for queue durableQueue on address address")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='CONSUME' for queue durableQueue on address address")); } // MANAGE @@ -614,7 +631,8 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { producer.send(session.createMessage(true)); Assert.fail("should throw exception here"); } catch (ActiveMQException e) { - assertTrue(e.getMessage().contains("User: first does not have permission='MANAGE' on address activemq.management")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='MANAGE' on address activemq.management")); } // BROWSE @@ -622,7 +640,8 @@ public void testJAASSecurityManagerAuthorizationNegative() throws Exception { ClientConsumer browser = session.createConsumer(DURABLE_QUEUE, true); Assert.fail("should throw exception here"); } catch (ActiveMQException e) { - assertTrue(e.getMessage().contains("User: first does not have permission='BROWSE' for queue durableQueue on address address")); + assertTrue(e.getMessage().contains("User: first")); + assertTrue(e.getMessage().contains("does not have permission='BROWSE' for queue durableQueue on address address")); } } @@ -645,14 +664,14 @@ private void internalJAASSecurityManagerAuthorizationSameAddressDifferentQueues( ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("PropertiesLogin"); ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set aRoles = new HashSet<>(); - aRoles.add(new Role(QUEUE_A.toString(), false, true, false, false, false, false, false, false, false, false)); + aRoles.add(new Role(QUEUE_A.toString(), false, true, false, false, false, false, false, false, false, false, false, false)); if (fqqnSyntax) { server.getConfiguration().putSecurityRoles(CompositeAddress.toFullyQualified(ADDRESS, QUEUE_A).toString(), aRoles); } else { server.getConfiguration().putSecurityRoles(ADDRESS.concat(".").concat(QUEUE_A).toString(), aRoles); } Set bRoles = new HashSet<>(); - bRoles.add(new Role(QUEUE_B.toString(), false, true, false, false, false, false, false, false, false, false)); + bRoles.add(new Role(QUEUE_B.toString(), false, true, false, false, false, false, false, false, false, false, false, false)); if (fqqnSyntax) { server.getConfiguration().putSecurityRoles(CompositeAddress.toFullyQualified(ADDRESS, QUEUE_B).toString(), bRoles); } else { @@ -709,11 +728,11 @@ public void testFallbackConsumerAuthorization() throws Exception { ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set aRoles = new HashSet<>(); - aRoles.add(new Role("xyz", true, true, true, true, true, true, true, true, true, true)); + aRoles.add(new Role("xyz", true, true, true, true, true, true, true, true, true, true, false, false)); server.getConfiguration().putSecurityRoles("a.*.b", aRoles); Set bRoles = new HashSet<>(); - bRoles.add(new Role("amq", true, true, true, true, true, true, true, true, true, true)); + bRoles.add(new Role("amq", true, true, true, true, true, true, true, true, true, true, false, false)); server.getConfiguration().putSecurityRoles("#", bRoles); server.start(); @@ -741,11 +760,11 @@ public void testJAASSecurityManagerFQQNAuthorizationWithJMS() throws Exception { ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set aRoles = new HashSet<>(); - aRoles.add(new Role(QUEUE_A.toString(), false, true, true, false, false, false, false, false, true, false)); + aRoles.add(new Role(QUEUE_A.toString(), false, true, true, false, false, false, false, false, true, false, false, false)); server.getConfiguration().putSecurityRoles(CompositeAddress.toFullyQualified(ADDRESS, QUEUE_A).toString(), aRoles); Set bRoles = new HashSet<>(); - bRoles.add(new Role(QUEUE_B.toString(), false, true, true, false, false, false, false, false, true, false)); + bRoles.add(new Role(QUEUE_B.toString(), false, true, true, false, false, false, false, false, true, false, false, false)); server.getConfiguration().putSecurityRoles(CompositeAddress.toFullyQualified(ADDRESS, QUEUE_B).toString(), bRoles); server.start(); @@ -815,7 +834,7 @@ public void testJAASSecurityManagerAuthorizationNegativeWithCerts() throws Excep server.getConfiguration().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params)); Set 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(); @@ -911,7 +930,7 @@ public void testJAASSecurityManagerAuthorizationPositive() throws Exception { ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("PropertiesLogin"); ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set 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(); @@ -1008,7 +1027,7 @@ protected void testJAASSecurityManagerAuthorizationPositiveWithCerts(String clie server.getConfiguration().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params)); Set 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(); @@ -1092,7 +1111,7 @@ public void testJAASSecurityManagerAuthorizationPositiveGuest() throws Exception ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager("GuestLogin"); ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(createDefaultInVMConfig().setSecurityEnabled(true), ManagementFactory.getPlatformMBeanServer(), securityManager, false)); Set roles = new HashSet<>(); - roles.add(new Role("bar", true, true, true, true, true, true, true, false, true, true)); + roles.add(new Role("bar", true, true, true, true, true, true, true, false, true, true, false, false)); server.getConfiguration().putSecurityRoles("#", roles); server.start(); @@ -1239,7 +1258,7 @@ public void testCreateDurableQueueWithRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false); + Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1258,7 +1277,7 @@ public void testCreateDurableQueueWithoutRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false); + Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1283,7 +1302,7 @@ public void testDeleteDurableQueueWithRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, true, true, false, false, false, false, true, true); + Role role = new Role("arole", false, false, true, true, false, false, false, false, true, true, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1302,7 +1321,7 @@ public void testDeleteDurableQueueWithoutRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false); + Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1329,7 +1348,7 @@ public void testCreateTempQueueWithRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, false, false, true, false, false, false, true, false); + Role role = new Role("arole", false, false, false, false, true, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1348,7 +1367,7 @@ public void testCreateTempQueueWithoutRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false); + Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1373,7 +1392,7 @@ public void testDeleteTempQueueWithRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, false, false, true, true, false, false, true, true); + Role role = new Role("arole", false, false, false, false, true, true, false, false, true, true, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1392,7 +1411,7 @@ public void testDeleteTempQueueWithoutRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, false, false, true, false, false, false, true, false); + Role role = new Role("arole", false, false, false, false, true, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1423,7 +1442,7 @@ public void testSendWithRole() throws Exception { securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", true, true, true, false, false, false, false, false, true, false); + Role role = new Role("arole", true, true, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); @@ -1455,7 +1474,7 @@ public void testSendWithRole() throws Exception { receivedMessage.acknowledge(); - role = new Role("arole", false, false, true, false, false, false, false, false, false, false); + role = new Role("arole", false, false, true, false, false, false, false, false, false, false, false, false); roles = new HashSet<>(); @@ -1482,7 +1501,7 @@ public void testSendWithoutRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false); + Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1510,7 +1529,7 @@ public void testNonBlockSendWithoutRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false); + Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); @@ -1536,8 +1555,8 @@ public void testCreateConsumerWithRole() throws Exception { securityManager.getConfiguration().addUser("guest", "guest"); securityManager.getConfiguration().addRole("guest", "guest"); securityManager.getConfiguration().setDefaultUser("guest"); - Role role = new Role("arole", false, true, false, false, false, false, false, false, false, false); - Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false); + Role role = new Role("arole", false, true, false, false, false, false, false, false, false, false, false, false); + Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(sendRole); roles.add(role); @@ -1564,8 +1583,8 @@ public void testCreateConsumerWithoutRole() throws Exception { securityManager.getConfiguration().addUser("guest", "guest"); securityManager.getConfiguration().addRole("guest", "guest"); securityManager.getConfiguration().setDefaultUser("guest"); - Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false); - Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false); + Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false, false, false); + Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(sendRole); roles.add(role); @@ -1599,9 +1618,9 @@ public void testSendMessageUpdateRoleCached() throws Exception { securityManager.getConfiguration().addUser("guest", "guest"); securityManager.getConfiguration().addRole("guest", "guest"); securityManager.getConfiguration().setDefaultUser("guest"); - Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false); - Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false); - Role receiveRole = new Role("receiver", false, true, false, false, false, false, false, false, false, false); + Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false, false, false); + Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false, false, false); + Role receiveRole = new Role("receiver", false, true, false, false, false, false, false, false, false, false, false, false); Set roles = new HashSet<>(); roles.add(sendRole); roles.add(role); @@ -1651,9 +1670,9 @@ public void testSendMessageUpdateRoleCached2() throws Exception { securityManager.getConfiguration().addUser("guest", "guest"); securityManager.getConfiguration().addRole("guest", "guest"); securityManager.getConfiguration().setDefaultUser("guest"); - Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false); - Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false); - Role receiveRole = new Role("receiver", false, true, false, false, false, false, false, false, false, false); + Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false, false, false); + Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false, false, false); + Role receiveRole = new Role("receiver", false, true, false, false, false, false, false, false, false, false, false, false); Set roles = new HashSet<>(); roles.add(sendRole); roles.add(role); @@ -1707,9 +1726,9 @@ public void testSendMessageUpdateSender() throws Exception { securityManager.getConfiguration().addUser("guest", "guest"); securityManager.getConfiguration().addRole("guest", "guest"); securityManager.getConfiguration().setDefaultUser("guest"); - Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false); - Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false); - Role receiveRole = new Role("receiver", false, true, false, false, false, false, false, false, false, false); + Role role = new Role("arole", false, false, false, false, false, false, false, false, false, false, false, false); + Role sendRole = new Role("guest", true, false, true, false, false, false, false, false, true, false, false, false); + Role receiveRole = new Role("receiver", false, true, false, false, false, false, false, false, false, false, false, false); Set roles = new HashSet<>(); roles.add(sendRole); roles.add(role); @@ -1802,7 +1821,7 @@ public void testSendManagementWithRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, false, false, false, false, true, false, false, false); + Role role = new Role("arole", false, false, false, false, false, false, true, false, false, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(configuration.getManagementAddress().toString(), roles); @@ -1823,7 +1842,7 @@ public void testSendManagementWithoutRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false); + Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(configuration.getManagementAddress().toString(), roles); @@ -1852,7 +1871,7 @@ public void testNonBlockSendManagementWithoutRole() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false); + Role role = new Role("arole", false, false, true, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(configuration.getManagementAddress().toString(), roles); @@ -1888,23 +1907,23 @@ public void testComplexRoles() throws Exception { securityManager.getConfiguration().addRole("frank", "user"); securityManager.getConfiguration().addRole("sam", "news-user"); securityManager.getConfiguration().addRole("sam", "user"); - Role all = new Role("all", true, true, true, true, true, true, true, true, true, true); + Role all = new Role("all", true, true, true, true, true, true, true, true, true, true, false, false); HierarchicalRepository> repository = server.getSecurityRepository(); Set add = new HashSet<>(); - add.add(new Role("user", true, true, true, true, true, true, false, true, true, true)); + add.add(new Role("user", true, true, true, true, true, true, false, true, true, true, false, false)); add.add(all); repository.addMatch("#", add); Set add1 = new HashSet<>(); add1.add(all); - add1.add(new Role("user", false, false, true, true, true, true, false, true, true, true)); - add1.add(new Role("europe-user", true, false, false, false, false, false, false, true, true, true)); - add1.add(new Role("news-user", false, true, false, false, false, false, false, true, true, true)); + add1.add(new Role("user", false, false, true, true, true, true, false, true, true, true, false, false)); + add1.add(new Role("europe-user", true, false, false, false, false, false, false, true, true, true, false, false)); + add1.add(new Role("news-user", false, true, false, false, false, false, false, true, true, true, false, false)); repository.addMatch("news.europe.#", add1); Set add2 = new HashSet<>(); add2.add(all); - add2.add(new Role("user", false, false, true, true, true, true, false, true, true, true)); - add2.add(new Role("us-user", true, false, false, false, false, false, false, true, true, true)); - add2.add(new Role("news-user", false, true, false, false, false, false, false, true, true, true)); + add2.add(new Role("user", false, false, true, true, true, true, false, true, true, true, false, false)); + add2.add(new Role("us-user", true, false, false, false, false, false, false, true, true, true, false, false)); + add2.add(new Role("news-user", false, true, false, false, false, false, false, true, true, true, false, false)); repository.addMatch("news.us.#", add2); ClientSession billConnection = null; ClientSession andrewConnection = null; @@ -2017,23 +2036,23 @@ public void testComplexRoles2() throws Exception { securityManager.getConfiguration().addRole("frank", "user"); securityManager.getConfiguration().addRole("sam", "news-user"); securityManager.getConfiguration().addRole("sam", "user"); - Role all = new Role("all", true, true, true, true, true, true, true, true, true, true); + Role all = new Role("all", true, true, true, true, true, true, true, true, true, true, false, false); HierarchicalRepository> repository = server.getSecurityRepository(); Set add = new HashSet<>(); - add.add(new Role("user", true, true, true, true, true, true, false, true, true, true)); + add.add(new Role("user", true, true, true, true, true, true, false, true, true, true, false, false)); add.add(all); repository.addMatch("#", add); Set add1 = new HashSet<>(); add1.add(all); - add1.add(new Role("user", false, false, true, true, true, true, false, true, true, true)); - add1.add(new Role("europe-user", true, false, false, false, false, false, false, true, true, true)); - add1.add(new Role("news-user", false, true, false, false, false, false, false, true, true, true)); + add1.add(new Role("user", false, false, true, true, true, true, false, true, true, true, false, false)); + add1.add(new Role("europe-user", true, false, false, false, false, false, false, true, true, true, false, false)); + add1.add(new Role("news-user", false, true, false, false, false, false, false, true, true, true, false, false)); repository.addMatch("news.europe.#", add1); Set add2 = new HashSet<>(); add2.add(all); - add2.add(new Role("user", false, false, true, true, true, true, false, true, true, true)); - add2.add(new Role("us-user", true, false, false, false, false, false, false, true, true, true)); - add2.add(new Role("news-user", false, true, false, false, false, false, false, true, true, true)); + add2.add(new Role("user", false, false, true, true, true, true, false, true, true, true, false, false)); + add2.add(new Role("us-user", true, false, false, false, false, false, false, true, true, true, false, false)); + add2.add(new Role("news-user", false, true, false, false, false, false, false, true, true, true, false, false)); repository.addMatch("news.us.#", add2); ClientSession billConnection = null; ClientSession andrewConnection = null; @@ -2594,7 +2613,7 @@ public void testReauthenticationIsCached() throws Exception { HierarchicalRepository> securityRepository = server.getSecurityRepository(); ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("auser", "pass"); - Role role = new Role("arole", true, false, false, false, false, false, false, false, true, false); + Role role = new Role("arole", true, false, false, false, false, false, false, false, true, false, false, false); Set roles = new HashSet<>(); roles.add(role); securityRepository.addMatch(SecurityTest.addressA, roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ResourceLimitTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ResourceLimitTest.java index cb5766e26b2..4aa24e02410 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ResourceLimitTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ResourceLimitTest.java @@ -62,7 +62,7 @@ public void setUp() throws Exception { ActiveMQJAASSecurityManager securityManager = (ActiveMQJAASSecurityManager) server.getSecurityManager(); securityManager.getConfiguration().addUser("myUser", "password"); securityManager.getConfiguration().addRole("myUser", "arole"); - Role role = new Role("arole", false, false, false, false, true, true, false, true, true, true); + Role role = new Role("arole", false, false, false, false, true, true, false, true, true, true, false, false); Set roles = new HashSet<>(); roles.add(role); server.getSecurityRepository().addMatch("#", roles); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ResourceLimitTestWithCerts.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ResourceLimitTestWithCerts.java index 14a383c9e5c..9d7b8d74958 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ResourceLimitTestWithCerts.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/server/ResourceLimitTestWithCerts.java @@ -81,7 +81,7 @@ public void setUp() throws Exception { server.getConfiguration().addAcceptorConfiguration(new TransportConfiguration(NETTY_ACCEPTOR_FACTORY, params)); Set 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(); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/DualAuthenticationTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/DualAuthenticationTest.java index d87b20a80be..9070554c929 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/DualAuthenticationTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/DualAuthenticationTest.java @@ -129,8 +129,8 @@ public void setUp() throws Exception { server = addServer(ActiveMQServers.newActiveMQServer(config, ManagementFactory.getPlatformMBeanServer(), securityManager, false)); HierarchicalRepository> securityRepository = server.getSecurityRepository(); - Role sendRole = new Role("producers", true, false, true, false, true, false, false, false, true, false); - Role receiveRole = new Role("consumers", false, true, false, false, false, false, false, false, false, false); + Role sendRole = new Role("producers", true, false, true, false, true, false, false, false, true, false, false, false); + Role receiveRole = new Role("consumers", false, true, false, false, false, false, false, false, false, false, false, false); Set roles = new HashSet<>(); roles.add(sendRole); roles.add(receiveRole); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/SslPEMTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/SslPEMTest.java index d5286b8c302..642279df5d5 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/SslPEMTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/ssl/SslPEMTest.java @@ -136,8 +136,8 @@ public void setUp() throws Exception { ActiveMQServer server = addServer(ActiveMQServers.newActiveMQServer(config, ManagementFactory.getPlatformMBeanServer(), securityManager, false)); HierarchicalRepository> securityRepository = server.getSecurityRepository(); - Role sendRole = new Role("producers", true, false, true, false, true, false, false, false, true, false); - Role receiveRole = new Role("consumers", false, true, false, false, false, false, false, false, false, false); + Role sendRole = new Role("producers", true, false, true, false, true, false, false, false, true, false, false, false); + Role receiveRole = new Role("consumers", false, true, false, false, false, false, false, false, false, false, false, false); Set roles = new HashSet<>(); roles.add(sendRole); roles.add(receiveRole); diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompAuditLoggingTest.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompAuditLoggingTest.java index fdc390ae08c..2ea276fad32 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompAuditLoggingTest.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompAuditLoggingTest.java @@ -59,7 +59,7 @@ protected ActiveMQServer createServer() throws Exception { securityManager.getConfiguration().addUser(user, pass); securityManager.getConfiguration().addRole(user, role); - server.getConfiguration().getSecurityRoles().put("#", new HashSet<>(Set.of(new Role(role, false, false, false, false, false, false, false, false, false, false)))); + server.getConfiguration().getSecurityRoles().put("#", new HashSet<>(Set.of(new Role(role, false, false, false, false, false, false, false, false, false, false, false, false)))); return server; } diff --git a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestBase.java b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestBase.java index 80e28e974fa..045e94cc6eb 100644 --- a/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestBase.java +++ b/tests/integration-tests/src/test/java/org/apache/activemq/artemis/tests/integration/stomp/StompTestBase.java @@ -214,7 +214,7 @@ protected ActiveMQServer createServer() throws Exception { final String role = "testRole"; securityManager.getConfiguration().addRole(defUser, role); - config.getSecurityRoles().put("#", new HashSet(Set.of(new Role(role, true, true, true, true, true, true, true, true, true, true)))); + config.getSecurityRoles().put("#", new HashSet(Set.of(new Role(role, true, true, true, true, true, true, true, true, true, true, false, false)))); } return activeMQServer; diff --git a/tests/integration-tests/src/test/resources/reload-security-settings-updated.xml b/tests/integration-tests/src/test/resources/reload-security-settings-updated.xml index 47d94b16d32..14e82c9d1ce 100644 --- a/tests/integration-tests/src/test/resources/reload-security-settings-updated.xml +++ b/tests/integration-tests/src/test/resources/reload-security-settings-updated.xml @@ -56,6 +56,8 @@ under the License. + + diff --git a/tests/smoke-tests/src/main/resources/servers/jmx-rbac-broker-security/broker.xml b/tests/smoke-tests/src/main/resources/servers/jmx-rbac-broker-security/broker.xml new file mode 100644 index 00000000000..39671a393a6 --- /dev/null +++ b/tests/smoke-tests/src/main/resources/servers/jmx-rbac-broker-security/broker.xml @@ -0,0 +1,148 @@ + + + + + + + + 0.0.0.0 + + true + + ./data/paging + + ./data/bindings + + ./data/journal + + ./data/large-messages + + true + + 2 + + 10 + + 4096 + + 10M + + 5000 + + 90 + + + + tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;amqpMinLargeMessageSize=102400;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpDuplicateDetection=true;supportAdvisory=false;suppressInternalManagementObjects=false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DLQ + ExpiryQueue + 0 + + -1 + 10 + PAGE + true + true + + + + DLQ + ExpiryQueue + 0 + + 10 + PAGE + true + true + false + false + + + 10M + + + + -1 + -1 + + + + -1 + 20M + + + + -1 + -1 + + + + +

+ + + +
+
+ + + +
+ + + + + diff --git a/tests/smoke-tests/src/main/resources/servers/jmx-rbac-broker-security/management.xml b/tests/smoke-tests/src/main/resources/servers/jmx-rbac-broker-security/management.xml new file mode 100644 index 00000000000..4a5ac25a216 --- /dev/null +++ b/tests/smoke-tests/src/main/resources/servers/jmx-rbac-broker-security/management.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACBrokerSecurityTest.java b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACBrokerSecurityTest.java new file mode 100644 index 00000000000..0b9a0b54fbb --- /dev/null +++ b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACBrokerSecurityTest.java @@ -0,0 +1,224 @@ +/* + * 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.smoke.jmxrbac; + +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerInvocationHandler; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; +import java.io.File; +import java.util.Collections; + +import org.apache.activemq.artemis.api.config.ActiveMQDefaultConfiguration; +import org.apache.activemq.artemis.api.core.Message; +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.AddressControl; +import org.apache.activemq.artemis.api.core.management.ObjectNameBuilder; +import org.apache.activemq.artemis.tests.smoke.common.SmokeTestBase; +import org.apache.activemq.artemis.util.ServerUtil; +import org.apache.activemq.artemis.utils.cli.helper.HelperCreate; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +// clone of JmxRBACTest with jmx security settings in broker.xml and new guard that delegates to security settings +// configured via -Djavax.management.builder.initial=org.apache.activemq.artemis.core.server.management.ArtemisRbacMBeanServerBuilder +public class JmxRBACBrokerSecurityTest extends SmokeTestBase { + + private static final String JMX_SERVER_HOSTNAME = "localhost"; + private static final int JMX_SERVER_PORT = 10099; + + public static final String BROKER_NAME = "0.0.0.0"; + + public static final String SERVER_NAME_0 = "jmx-rbac-broker-security"; + + public static final String SERVER_ADMIN = "admin"; + public static final String SERVER_USER = "user"; + + public static final String ADDRESS_TEST = "TEST"; + + @BeforeClass + public static void createServers() throws Exception { + + File server0Location = getFileServerLocation(SERVER_NAME_0); + deleteDirectory(server0Location); + + { + HelperCreate cliCreateServer = new HelperCreate(); + cliCreateServer.setRole("amq").setUser("admin").setPassword("admin").setAllowAnonymous(false).setNoWeb(true).setArtemisInstance(server0Location). + setConfiguration("./src/main/resources/servers/jmx-rbac-broker-security").setArgs("--java-options", "-Djava.rmi.server.hostname=localhost -Djavax.management.builder.initial=org.apache.activemq.artemis.core.server.management.ArtemisRbacMBeanServerBuilder"); + cliCreateServer.createServer(); + } + } + + + @Before + public void before() throws Exception { + cleanupData(SERVER_NAME_0); + disableCheckThread(); + startServer(SERVER_NAME_0, 0, 0); + ServerUtil.waitForServerToStart(0, SERVER_ADMIN, SERVER_ADMIN, 30000); + } + + @Test + public void testManagementRoleAccess() throws Exception { + + // I don't specify both ports here manually on purpose. See actual RMI registry connection port extraction below. + String urlString = "service:jmx:rmi:///jndi/rmi://" + JMX_SERVER_HOSTNAME + ":" + JMX_SERVER_PORT + "/jmxrmi"; + + JMXServiceURL url = new JMXServiceURL(urlString); + JMXConnector jmxConnector; + + try { + //Connect using the admin. + jmxConnector = JMXConnectorFactory.connect(url, Collections.singletonMap( + "jmx.remote.credentials", new String[] {SERVER_ADMIN, SERVER_ADMIN})); + System.out.println("Successfully connected to: " + urlString); + } catch (Exception e) { + jmxConnector = null; + e.printStackTrace(); + Assert.fail(e.getMessage()); + } + + try { + //Create an user. + MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); + ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), BROKER_NAME, true); + ActiveMQServerControl activeMQServerControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false); + ObjectName memoryObjectName = new ObjectName("java.lang:type=Memory"); + + try { + activeMQServerControl.removeUser(SERVER_USER); + } catch (Exception ignore) { + } + activeMQServerControl.addUser(SERVER_USER, SERVER_USER, "amq-user", true); + + activeMQServerControl.getVersion(); + + try { + mBeanServerConnection.invoke(memoryObjectName, "gc", null, null); + Assert.fail(SERVER_ADMIN + " should not access to " + memoryObjectName); + } catch (Exception e) { + Assert.assertEquals(SecurityException.class, e.getClass()); + } + } finally { + jmxConnector.close(); + } + + try { + //Connect using an user. + jmxConnector = JMXConnectorFactory.connect(url, Collections.singletonMap( + "jmx.remote.credentials", new String[] {SERVER_USER, SERVER_USER})); + System.out.println("Successfully connected to: " + urlString); + } catch (Exception e) { + jmxConnector = null; + e.printStackTrace(); + Assert.fail(e.getMessage()); + } + + + try { + MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); + ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), BROKER_NAME, true); + ActiveMQServerControl activeMQServerControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false); + ObjectName memoryObjectName = new ObjectName("java.lang:type=Memory"); + + mBeanServerConnection.invoke(memoryObjectName, "gc", null, null); + + try { + activeMQServerControl.getVersion(); + Assert.fail(SERVER_USER + " should not access to " + objectNameBuilder.getActiveMQServerObjectName()); + } catch (Exception e) { + Assert.assertEquals(SecurityException.class, e.getClass()); + } + } finally { + jmxConnector.close(); + } + } + + @Test + public void testSendMessageWithoutUserAndPassword() throws Exception { + + // I don't specify both ports here manually on purpose. See actual RMI registry connection port extraction below. + String urlString = "service:jmx:rmi:///jndi/rmi://" + JMX_SERVER_HOSTNAME + ":" + JMX_SERVER_PORT + "/jmxrmi"; + + JMXServiceURL url = new JMXServiceURL(urlString); + JMXConnector jmxConnector; + + try { + //Connect using the admin. + jmxConnector = JMXConnectorFactory.connect(url, Collections.singletonMap( + "jmx.remote.credentials", new String[] {SERVER_ADMIN, SERVER_ADMIN})); + System.out.println("Successfully connected to: " + urlString); + } catch (Exception e) { + jmxConnector = null; + e.printStackTrace(); + Assert.fail(e.getMessage()); + } + + try { + MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); + ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), BROKER_NAME, true); + ActiveMQServerControl activeMQServerControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getActiveMQServerObjectName(), ActiveMQServerControl.class, false); + + activeMQServerControl.createAddress(ADDRESS_TEST, RoutingType.MULTICAST.name()); + AddressControl testAddressControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getAddressObjectName(SimpleString.toSimpleString(ADDRESS_TEST)), AddressControl.class, false); + + testAddressControl.sendMessage(null, Message.TEXT_TYPE, ADDRESS_TEST, true, null, null); + + + try { + activeMQServerControl.removeUser(SERVER_USER); + } catch (Exception ignore) { + } + activeMQServerControl.addUser(SERVER_USER, SERVER_USER, "amq-user", true); + } finally { + jmxConnector.close(); + } + + try { + //Connect using an user. + jmxConnector = JMXConnectorFactory.connect(url, Collections.singletonMap( + "jmx.remote.credentials", new String[] {SERVER_USER, SERVER_USER})); + System.out.println("Successfully connected to: " + urlString); + } catch (Exception e) { + jmxConnector = null; + e.printStackTrace(); + Assert.fail(e.getMessage()); + } + + try { + MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection(); + ObjectNameBuilder objectNameBuilder = ObjectNameBuilder.create(ActiveMQDefaultConfiguration.getDefaultJmxDomain(), BROKER_NAME, true); + AddressControl testAddressControl = MBeanServerInvocationHandler.newProxyInstance(mBeanServerConnection, objectNameBuilder.getAddressObjectName(SimpleString.toSimpleString("TEST")), AddressControl.class, false); + + try { + testAddressControl.sendMessage(null, Message.TEXT_TYPE, ADDRESS_TEST, true, null, null); + Assert.fail(SERVER_USER + " should not have permissions to send a message to the address " + ADDRESS_TEST); + } catch (Exception e) { + Assert.assertEquals(SecurityException.class, e.getClass()); + } + } finally { + jmxConnector.close(); + } + } +} diff --git a/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACTest.java b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACTest.java index 573c63bf626..94eca1018b4 100644 --- a/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACTest.java +++ b/tests/smoke-tests/src/test/java/org/apache/activemq/artemis/tests/smoke/jmxrbac/JmxRBACTest.java @@ -81,8 +81,6 @@ public void before() throws Exception { @Test public void testManagementRoleAccess() throws Exception { - // Without this, the RMI server would bind to the default interface IP (the user's local IP mostly) - System.setProperty("java.rmi.server.hostname", JMX_SERVER_HOSTNAME); // I don't specify both ports here manually on purpose. See actual RMI registry connection port extraction below. String urlString = "service:jmx:rmi:///jndi/rmi://" + JMX_SERVER_HOSTNAME + ":" + JMX_SERVER_PORT + "/jmxrmi"; @@ -159,8 +157,6 @@ public void testManagementRoleAccess() throws Exception { @Test public void testSendMessageWithoutUserAndPassword() throws Exception { - // Without this, the RMI server would bind to the default interface IP (the user's local IP mostly) - System.setProperty("java.rmi.server.hostname", JMX_SERVER_HOSTNAME); // I don't specify both ports here manually on purpose. See actual RMI registry connection port extraction below. String urlString = "service:jmx:rmi:///jndi/rmi://" + JMX_SERVER_HOSTNAME + ":" + JMX_SERVER_PORT + "/jmxrmi"; diff --git a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/security/impl/ActiveMQSecurityManagerImplTest.java b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/security/impl/ActiveMQSecurityManagerImplTest.java index eccdb5f5d5f..c0b9d12f612 100644 --- a/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/security/impl/ActiveMQSecurityManagerImplTest.java +++ b/tests/unit-tests/src/test/java/org/apache/activemq/artemis/tests/unit/core/security/impl/ActiveMQSecurityManagerImplTest.java @@ -59,22 +59,22 @@ public void testDefaultSecurity() { Assert.assertTrue(securityManager.validateUser("guest", "password")); Assert.assertFalse(securityManager.validateUser(null, "wrongpass")); HashSet roles = new HashSet<>(); - roles.add(new Role("guest", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("guest", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertTrue(securityManager.validateUserAndRole(null, null, roles, CheckType.CREATE_DURABLE_QUEUE)); Assert.assertTrue(securityManager.validateUserAndRole(null, null, roles, CheckType.SEND)); Assert.assertTrue(securityManager.validateUserAndRole(null, null, roles, CheckType.CONSUME)); roles = new HashSet<>(); - roles.add(new Role("guest", true, true, false, true, true, true, true, true, true, true)); + roles.add(new Role("guest", true, true, false, true, true, true, true, true, true, true, false, false)); Assert.assertFalse(securityManager.validateUserAndRole(null, null, roles, CheckType.CREATE_DURABLE_QUEUE)); Assert.assertTrue(securityManager.validateUserAndRole(null, null, roles, CheckType.SEND)); Assert.assertTrue(securityManager.validateUserAndRole(null, null, roles, CheckType.CONSUME)); roles = new HashSet<>(); - roles.add(new Role("guest", true, false, false, true, true, true, true, true, true, true)); + roles.add(new Role("guest", true, false, false, true, true, true, true, true, true, true, false, false)); Assert.assertFalse(securityManager.validateUserAndRole(null, null, roles, CheckType.CREATE_DURABLE_QUEUE)); Assert.assertTrue(securityManager.validateUserAndRole(null, null, roles, CheckType.SEND)); Assert.assertFalse(securityManager.validateUserAndRole(null, null, roles, CheckType.CONSUME)); roles = new HashSet<>(); - roles.add(new Role("guest", false, false, false, true, true, true, true, true, true, true)); + roles.add(new Role("guest", false, false, false, true, true, true, true, true, true, true, false, false)); Assert.assertFalse(securityManager.validateUserAndRole(null, null, roles, CheckType.CREATE_DURABLE_QUEUE)); Assert.assertFalse(securityManager.validateUserAndRole(null, null, roles, CheckType.SEND)); Assert.assertFalse(securityManager.validateUserAndRole(null, null, roles, CheckType.CONSUME)); @@ -124,19 +124,19 @@ public void testAddingRoles() { securityManager.getConfiguration().addRole("newuser1", "role3"); securityManager.getConfiguration().addRole("newuser1", "role4"); HashSet roles = new HashSet<>(); - roles.add(new Role("role1", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role1", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertTrue(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); roles = new HashSet<>(); - roles.add(new Role("role2", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role2", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertTrue(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); roles = new HashSet<>(); - roles.add(new Role("role3", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role3", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertTrue(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); roles = new HashSet<>(); - roles.add(new Role("role4", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role4", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertTrue(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); roles = new HashSet<>(); - roles.add(new Role("role5", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role5", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertFalse(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); } @@ -150,19 +150,19 @@ public void testRemovingRoles() { securityManager.getConfiguration().removeRole("newuser1", "role2"); securityManager.getConfiguration().removeRole("newuser1", "role4"); HashSet roles = new HashSet<>(); - roles.add(new Role("role1", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role1", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertTrue(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); roles = new HashSet<>(); - roles.add(new Role("role2", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role2", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertFalse(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); roles = new HashSet<>(); - roles.add(new Role("role3", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role3", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertTrue(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); roles = new HashSet<>(); - roles.add(new Role("role4", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role4", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertFalse(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); roles = new HashSet<>(); - roles.add(new Role("role5", true, true, true, true, true, true, true, true, true, true)); + roles.add(new Role("role5", true, true, true, true, true, true, true, true, true, true, false, false)); Assert.assertFalse(securityManager.validateUserAndRole("newuser1", "newpassword1", roles, CheckType.SEND)); } }