ARTEMIS-168 - pluggable authorization
This commit is contained in:
parent
d03541a460
commit
fe4dafedcb
|
@ -27,6 +27,7 @@ import org.apache.activemq.artemis.api.core.SimpleString;
|
||||||
import org.apache.activemq.artemis.api.core.TransportConfiguration;
|
import org.apache.activemq.artemis.api.core.TransportConfiguration;
|
||||||
import org.apache.activemq.artemis.core.security.Role;
|
import org.apache.activemq.artemis.core.security.Role;
|
||||||
import org.apache.activemq.artemis.core.server.JournalType;
|
import org.apache.activemq.artemis.core.server.JournalType;
|
||||||
|
import org.apache.activemq.artemis.core.server.SecuritySettingPlugin;
|
||||||
import org.apache.activemq.artemis.core.server.group.impl.GroupingHandlerConfiguration;
|
import org.apache.activemq.artemis.core.server.group.impl.GroupingHandlerConfiguration;
|
||||||
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
|
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
|
||||||
import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
|
import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
|
||||||
|
@ -852,11 +853,17 @@ public interface Configuration {
|
||||||
|
|
||||||
Configuration addConnectorServiceConfiguration(ConnectorServiceConfiguration config);
|
Configuration addConnectorServiceConfiguration(ConnectorServiceConfiguration config);
|
||||||
|
|
||||||
|
Configuration setSecuritySettingPlugins(final List<SecuritySettingPlugin> plugins);
|
||||||
|
|
||||||
|
Configuration addSecuritySettingPlugin(final SecuritySettingPlugin plugin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return list of {@link ConnectorServiceConfiguration}
|
* @return list of {@link ConnectorServiceConfiguration}
|
||||||
*/
|
*/
|
||||||
List<ConnectorServiceConfiguration> getConnectorServiceConfigurations();
|
List<ConnectorServiceConfiguration> getConnectorServiceConfigurations();
|
||||||
|
|
||||||
|
List<SecuritySettingPlugin> getSecuritySettingPlugins();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default password decoder
|
* The default password decoder
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -48,6 +48,7 @@ import org.apache.activemq.artemis.core.config.ha.ReplicaPolicyConfiguration;
|
||||||
import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration;
|
import org.apache.activemq.artemis.core.config.ha.ReplicatedPolicyConfiguration;
|
||||||
import org.apache.activemq.artemis.core.security.Role;
|
import org.apache.activemq.artemis.core.security.Role;
|
||||||
import org.apache.activemq.artemis.core.server.JournalType;
|
import org.apache.activemq.artemis.core.server.JournalType;
|
||||||
|
import org.apache.activemq.artemis.core.server.SecuritySettingPlugin;
|
||||||
import org.apache.activemq.artemis.core.server.group.impl.GroupingHandlerConfiguration;
|
import org.apache.activemq.artemis.core.server.group.impl.GroupingHandlerConfiguration;
|
||||||
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
|
import org.apache.activemq.artemis.core.settings.impl.AddressSettings;
|
||||||
import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
|
import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
|
||||||
|
@ -209,6 +210,8 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
||||||
|
|
||||||
private Map<String, Set<Role>> securitySettings = new HashMap<String, Set<Role>>();
|
private Map<String, Set<Role>> securitySettings = new HashMap<String, Set<Role>>();
|
||||||
|
|
||||||
|
private List<SecuritySettingPlugin> securitySettingPlugins = new ArrayList<SecuritySettingPlugin>();
|
||||||
|
|
||||||
protected List<ConnectorServiceConfiguration> connectorServiceConfigurations = new ArrayList<ConnectorServiceConfiguration>();
|
protected List<ConnectorServiceConfiguration> connectorServiceConfigurations = new ArrayList<ConnectorServiceConfiguration>();
|
||||||
|
|
||||||
private boolean maskPassword = ActiveMQDefaultConfiguration.isDefaultMaskPassword();
|
private boolean maskPassword = ActiveMQDefaultConfiguration.isDefaultMaskPassword();
|
||||||
|
@ -964,6 +967,9 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Set<Role>> getSecurityRoles() {
|
public Map<String, Set<Role>> getSecurityRoles() {
|
||||||
|
for (SecuritySettingPlugin securitySettingPlugin : securitySettingPlugins) {
|
||||||
|
securitySettings.putAll(securitySettingPlugin.getSecurityRoles());
|
||||||
|
}
|
||||||
return securitySettings;
|
return securitySettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -977,6 +983,10 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
||||||
return this.connectorServiceConfigurations;
|
return this.connectorServiceConfigurations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<SecuritySettingPlugin> getSecuritySettingPlugins() {
|
||||||
|
return this.securitySettingPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
public File getBrokerInstance() {
|
public File getBrokerInstance() {
|
||||||
if (artemisInstance != null) {
|
if (artemisInstance != null) {
|
||||||
return artemisInstance;
|
return artemisInstance;
|
||||||
|
@ -1036,6 +1046,16 @@ public class ConfigurationImpl implements Configuration, Serializable {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ConfigurationImpl setSecuritySettingPlugins(final List<SecuritySettingPlugin> plugins) {
|
||||||
|
this.securitySettingPlugins = plugins;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConfigurationImpl addSecuritySettingPlugin(final SecuritySettingPlugin plugin) {
|
||||||
|
this.securitySettingPlugins.add(plugin);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isMaskPassword() {
|
public boolean isMaskPassword() {
|
||||||
return maskPassword;
|
return maskPassword;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,8 @@ package org.apache.activemq.artemis.core.deployers.impl;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.security.AccessController;
|
||||||
|
import java.security.PrivilegedAction;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -56,6 +58,7 @@ import org.apache.activemq.artemis.core.io.aio.AIOSequentialFileFactory;
|
||||||
import org.apache.activemq.artemis.core.security.Role;
|
import org.apache.activemq.artemis.core.security.Role;
|
||||||
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||||
import org.apache.activemq.artemis.core.server.JournalType;
|
import org.apache.activemq.artemis.core.server.JournalType;
|
||||||
|
import org.apache.activemq.artemis.core.server.SecuritySettingPlugin;
|
||||||
import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType;
|
import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType;
|
||||||
import org.apache.activemq.artemis.core.server.group.impl.GroupingHandlerConfiguration;
|
import org.apache.activemq.artemis.core.server.group.impl.GroupingHandlerConfiguration;
|
||||||
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy;
|
import org.apache.activemq.artemis.core.settings.impl.AddressFullMessagePolicy;
|
||||||
|
@ -64,6 +67,7 @@ import org.apache.activemq.artemis.core.settings.impl.ResourceLimitSettings;
|
||||||
import org.apache.activemq.artemis.core.settings.impl.SlowConsumerPolicy;
|
import org.apache.activemq.artemis.core.settings.impl.SlowConsumerPolicy;
|
||||||
import org.apache.activemq.artemis.uri.AcceptorTransportConfigurationParser;
|
import org.apache.activemq.artemis.uri.AcceptorTransportConfigurationParser;
|
||||||
import org.apache.activemq.artemis.uri.ConnectorTransportConfigurationParser;
|
import org.apache.activemq.artemis.uri.ConnectorTransportConfigurationParser;
|
||||||
|
import org.apache.activemq.artemis.utils.ClassloadingUtil;
|
||||||
import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;
|
import org.apache.activemq.artemis.utils.DefaultSensitiveStringCodec;
|
||||||
import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
|
import org.apache.activemq.artemis.utils.PasswordMaskingUtil;
|
||||||
import org.apache.activemq.artemis.utils.SensitiveDataCodec;
|
import org.apache.activemq.artemis.utils.SensitiveDataCodec;
|
||||||
|
@ -82,12 +86,20 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
||||||
// Security Parsing
|
// Security Parsing
|
||||||
public static final String SECURITY_ELEMENT_NAME = "security-setting";
|
public static final String SECURITY_ELEMENT_NAME = "security-setting";
|
||||||
|
|
||||||
|
public static final String SECURITY_PLUGIN_ELEMENT_NAME = "security-setting-plugin";
|
||||||
|
|
||||||
private static final String PERMISSION_ELEMENT_NAME = "permission";
|
private static final String PERMISSION_ELEMENT_NAME = "permission";
|
||||||
|
|
||||||
|
private static final String SETTING_ELEMENT_NAME = "setting";
|
||||||
|
|
||||||
private static final String TYPE_ATTR_NAME = "type";
|
private static final String TYPE_ATTR_NAME = "type";
|
||||||
|
|
||||||
private static final String ROLES_ATTR_NAME = "roles";
|
private static final String ROLES_ATTR_NAME = "roles";
|
||||||
|
|
||||||
|
private static final String NAME_ATTR_NAME = "name";
|
||||||
|
|
||||||
|
private static final String VALUE_ATTR_NAME = "value";
|
||||||
|
|
||||||
static final String CREATEDURABLEQUEUE_NAME = "createDurableQueue";
|
static final String CREATEDURABLEQUEUE_NAME = "createDurableQueue";
|
||||||
|
|
||||||
private static final String DELETEDURABLEQUEUE_NAME = "deleteDurableQueue";
|
private static final String DELETEDURABLEQUEUE_NAME = "deleteDurableQueue";
|
||||||
|
@ -517,6 +529,11 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
||||||
Pair<String, Set<Role>> securityItem = parseSecurityRoles(list.item(i));
|
Pair<String, Set<Role>> securityItem = parseSecurityRoles(list.item(i));
|
||||||
config.getSecurityRoles().put(securityItem.getA(), securityItem.getB());
|
config.getSecurityRoles().put(securityItem.getA(), securityItem.getB());
|
||||||
}
|
}
|
||||||
|
list = node.getElementsByTagName(SECURITY_PLUGIN_ELEMENT_NAME);
|
||||||
|
for (int i = 0; i < list.getLength(); i++) {
|
||||||
|
Pair<SecuritySettingPlugin, Map<String, String>> securityItem = parseSecuritySettingPlugins(list.item(i));
|
||||||
|
config.addSecuritySettingPlugin(securityItem.getA().init(securityItem.getB()).populateSecurityRoles());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,6 +660,29 @@ public final class FileConfigurationParser extends XMLConfigurationUtil {
|
||||||
return securityMatch;
|
return securityMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Pair<SecuritySettingPlugin,Map<String,String>> parseSecuritySettingPlugins(Node item) {
|
||||||
|
final String clazz = item.getAttributes().getNamedItem("class-name").getNodeValue();
|
||||||
|
final Map<String, String> settings = new HashMap<>();
|
||||||
|
NodeList children = item.getChildNodes();
|
||||||
|
for (int j = 0; j < children.getLength(); j++) {
|
||||||
|
Node child = children.item(j);
|
||||||
|
final String nodeName = child.getNodeName();
|
||||||
|
if (SETTING_ELEMENT_NAME.equalsIgnoreCase(nodeName)) {
|
||||||
|
final String settingName = getAttributeValue(child, NAME_ATTR_NAME);
|
||||||
|
final String settingValue = getAttributeValue(child, VALUE_ATTR_NAME);
|
||||||
|
settings.put(settingName, settingValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SecuritySettingPlugin securitySettingPlugin = AccessController.doPrivileged(new PrivilegedAction<SecuritySettingPlugin>() {
|
||||||
|
public SecuritySettingPlugin run() {
|
||||||
|
return (SecuritySettingPlugin) ClassloadingUtil.newInstanceFromClassLoader(clazz);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Pair<SecuritySettingPlugin, Map<String, String>>(securitySettingPlugin, settings);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param node
|
* @param node
|
||||||
* @return
|
* @return
|
||||||
|
|
|
@ -301,6 +301,10 @@ public interface ActiveMQServerLogger extends BasicLogger {
|
||||||
@Message(id = 221050, value = "Activating Shared Store Slave", format = Message.Format.MESSAGE_FORMAT)
|
@Message(id = 221050, value = "Activating Shared Store Slave", format = Message.Format.MESSAGE_FORMAT)
|
||||||
void activatingSharedStoreSlave();
|
void activatingSharedStoreSlave();
|
||||||
|
|
||||||
|
@LogMessage(level = Logger.Level.INFO)
|
||||||
|
@Message(id = 221051, value = "Populating security roles from LDAP at: {0}", format = Message.Format.MESSAGE_FORMAT)
|
||||||
|
void populatingSecurityRolesFromLDAP(String url);
|
||||||
|
|
||||||
@LogMessage(level = Logger.Level.WARN)
|
@LogMessage(level = Logger.Level.WARN)
|
||||||
@Message(id = 222000, value = "ActiveMQServer is being finalized and has not been stopped. Please remember to stop the server before letting it go out of scope",
|
@Message(id = 222000, value = "ActiveMQServer is being finalized and has not been stopped. Please remember to stop the server before letting it go out of scope",
|
||||||
format = Message.Format.MESSAGE_FORMAT)
|
format = Message.Format.MESSAGE_FORMAT)
|
||||||
|
@ -1445,4 +1449,12 @@ public interface ActiveMQServerLogger extends BasicLogger {
|
||||||
@LogMessage(level = Logger.Level.ERROR)
|
@LogMessage(level = Logger.Level.ERROR)
|
||||||
@Message(id = 224065, value = "Failed to remove auto-created queue {0}", format = Message.Format.MESSAGE_FORMAT)
|
@Message(id = 224065, value = "Failed to remove auto-created queue {0}", format = Message.Format.MESSAGE_FORMAT)
|
||||||
void errorRemovingAutoCreatedQueue(@Cause Exception e, SimpleString bindingName);
|
void errorRemovingAutoCreatedQueue(@Cause Exception e, SimpleString bindingName);
|
||||||
|
|
||||||
|
@LogMessage(level = Logger.Level.ERROR)
|
||||||
|
@Message(id = 224066, value = "Error opening context for LDAP", format = Message.Format.MESSAGE_FORMAT)
|
||||||
|
void errorOpeningContextForLDAP(@Cause Exception e);
|
||||||
|
|
||||||
|
@LogMessage(level = Logger.Level.ERROR)
|
||||||
|
@Message(id = 224067, value = "Error populating security roles from LDAP", format = Message.Format.MESSAGE_FORMAT)
|
||||||
|
void errorPopulatingSecurityRolesFromLDAP(@Cause Exception e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.activemq.artemis.core.security.Role;
|
||||||
|
|
||||||
|
public interface SecuritySettingPlugin extends Serializable {
|
||||||
|
/**
|
||||||
|
* Initialize the plugin with the given configuration options. This method is called by the broker when the file-based
|
||||||
|
* configuration is read (see {@code org.apache.activemq.artemis.core.deployers.impl.FileConfigurationParser#parseSecurity(org.w3c.dom.Element, org.apache.activemq.artemis.core.config.Configuration)}.
|
||||||
|
* If you're creating/configuring the plugin programmatically then the recommended approach is to simply use the plugin's
|
||||||
|
* getters/setters rather than this method.
|
||||||
|
*
|
||||||
|
* @param options name/value pairs used to configure the SecuritySettingPlugin instance
|
||||||
|
* @return {@code this} instance
|
||||||
|
*/
|
||||||
|
SecuritySettingPlugin init(Map<String, String> options);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Once {@code #populateSecurityRoles} is invoked this method should return the security role information from the
|
||||||
|
* external environment (e.g. file, LDAP, etc.).
|
||||||
|
*
|
||||||
|
* @return the Map's key corresponds to the "match" for the security setting and the corresponding value is the set of
|
||||||
|
* {@code org.apache.activemq.artemis.core.security.Role} objects defining the appropriate authorization
|
||||||
|
*/
|
||||||
|
Map<String, Set<Role>> getSecurityRoles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the security role information from the external environment (e.g. file, LDAP, etc.). This method should put
|
||||||
|
* the security role information in the variable that is returned by {@code #getSecurityRoles()}. This method is
|
||||||
|
* called by the broker when the file-based configuration is read (see {@code org.apache.activemq.artemis.core.deployers.impl.FileConfigurationParser#parseSecurity(org.w3c.dom.Element, org.apache.activemq.artemis.core.config.Configuration)}
|
||||||
|
* so that later when {@code #getSecurityRoles()} is called by {@code org.apache.activemq.artemis.core.server.impl.ActiveMQServerImpl#deploySecurity()}
|
||||||
|
* the necessary information will be present. If you're creating/configuring the plugin programmatically then you'll
|
||||||
|
* want to invoke this method soon after instantiating and configuring it.
|
||||||
|
*
|
||||||
|
* @return {@code this} instance
|
||||||
|
*/
|
||||||
|
SecuritySettingPlugin populateSecurityRoles();
|
||||||
|
}
|
|
@ -0,0 +1,321 @@
|
||||||
|
/*
|
||||||
|
* 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.impl;
|
||||||
|
|
||||||
|
import javax.naming.Context;
|
||||||
|
import javax.naming.NamingEnumeration;
|
||||||
|
import javax.naming.NamingException;
|
||||||
|
import javax.naming.directory.Attribute;
|
||||||
|
import javax.naming.directory.Attributes;
|
||||||
|
import javax.naming.directory.DirContext;
|
||||||
|
import javax.naming.directory.InitialDirContext;
|
||||||
|
import javax.naming.directory.SearchControls;
|
||||||
|
import javax.naming.directory.SearchResult;
|
||||||
|
import javax.naming.ldap.LdapName;
|
||||||
|
import javax.naming.ldap.Rdn;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.activemq.artemis.core.security.Role;
|
||||||
|
import org.apache.activemq.artemis.core.server.SecuritySettingPlugin;
|
||||||
|
import org.apache.activemq.artemis.core.server.ActiveMQServerLogger;
|
||||||
|
|
||||||
|
public class LegacyLDAPSecuritySettingPlugin implements SecuritySettingPlugin {
|
||||||
|
private static final long serialVersionUID = 4793109879399750045L;
|
||||||
|
|
||||||
|
public static final String INITIAL_CONTEXT_FACTORY = "initialContextFactory";
|
||||||
|
public static final String CONNECTION_URL = "connectionURL";
|
||||||
|
public static final String CONNECTION_USERNAME = "connectionUsername";
|
||||||
|
public static final String CONNECTION_PASSWORD = "connectionPassword";
|
||||||
|
public static final String CONNECTION_PROTOCOL = "connectionProtocol";
|
||||||
|
public static final String AUTHENTICATION = "authentication";
|
||||||
|
public static final String ROLE_ATTRIBUTE = "roleAttribute";
|
||||||
|
public static final String FILTER = "filter";
|
||||||
|
public static final String DESTINATION_BASE = "destinationBase";
|
||||||
|
public static final String ADMIN_PERMISSION_VALUE = "adminPermissionValue";
|
||||||
|
public static final String READ_PERMISSION_VALUE = "readPermissionValue";
|
||||||
|
public static final String WRITE_PERMISSION_VALUE = "writePermissionValue";
|
||||||
|
|
||||||
|
private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
|
||||||
|
private String connectionURL = "ldap://localhost:1024";
|
||||||
|
private String connectionUsername;
|
||||||
|
private String connectionPassword;
|
||||||
|
private String connectionProtocol;
|
||||||
|
private String authentication = "simple";
|
||||||
|
private String destinationBase = "ou=destinations,o=ActiveMQ,ou=system";
|
||||||
|
private String filter = "(cn=*)";
|
||||||
|
private String roleAttribute = "uniqueMember";
|
||||||
|
private String adminPermissionValue = "admin";
|
||||||
|
private String readPermissionValue = "read";
|
||||||
|
private String writePermissionValue = "write";
|
||||||
|
|
||||||
|
private DirContext context;
|
||||||
|
private Map<String, Set<Role>> securityRoles = new HashMap<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LegacyLDAPSecuritySettingPlugin init(Map<String, String> options) {
|
||||||
|
if (options != null) {
|
||||||
|
initialContextFactory = options.get(INITIAL_CONTEXT_FACTORY);
|
||||||
|
connectionURL = options.get(CONNECTION_URL);
|
||||||
|
connectionUsername = options.get(CONNECTION_USERNAME);
|
||||||
|
connectionPassword = options.get(CONNECTION_PASSWORD);
|
||||||
|
connectionProtocol = options.get(CONNECTION_PROTOCOL);
|
||||||
|
authentication = options.get(AUTHENTICATION);
|
||||||
|
destinationBase = options.get(DESTINATION_BASE);
|
||||||
|
filter = options.get(FILTER);
|
||||||
|
roleAttribute = options.get(ROLE_ATTRIBUTE);
|
||||||
|
adminPermissionValue = options.get(ADMIN_PERMISSION_VALUE);
|
||||||
|
readPermissionValue = options.get(READ_PERMISSION_VALUE);
|
||||||
|
writePermissionValue = options.get(WRITE_PERMISSION_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoleAttribute() {
|
||||||
|
return roleAttribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SecuritySettingPlugin setRoleAttribute(String roleAttribute) {
|
||||||
|
this.roleAttribute = roleAttribute;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFilter() {
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setFilter(String filter) {
|
||||||
|
this.filter = filter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestinationBase() {
|
||||||
|
return destinationBase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setDestinationBase(String destinationBase) {
|
||||||
|
this.destinationBase = destinationBase;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAuthentication() {
|
||||||
|
return authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setAuthentication(String authentication) {
|
||||||
|
this.authentication = authentication;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConnectionPassword() {
|
||||||
|
return connectionPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setConnectionPassword(String connectionPassword) {
|
||||||
|
this.connectionPassword = connectionPassword;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConnectionProtocol() {
|
||||||
|
return connectionProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setConnectionProtocol(String connectionProtocol) {
|
||||||
|
this.connectionProtocol = connectionProtocol;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConnectionURL() {
|
||||||
|
return connectionURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setConnectionURL(String connectionURL) {
|
||||||
|
this.connectionURL = connectionURL;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConnectionUsername() {
|
||||||
|
return connectionUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setConnectionUsername(String connectionUsername) {
|
||||||
|
this.connectionUsername = connectionUsername;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInitialContextFactory() {
|
||||||
|
return initialContextFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAdminPermissionValue() {
|
||||||
|
return adminPermissionValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setAdminPermissionValue(String adminPermissionValue) {
|
||||||
|
this.adminPermissionValue = adminPermissionValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getReadPermissionValue() {
|
||||||
|
return readPermissionValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setReadPermissionValue(String readPermissionValue) {
|
||||||
|
this.readPermissionValue = readPermissionValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWritePermissionValue() {
|
||||||
|
return writePermissionValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setWritePermissionValue(String writePermissionValue) {
|
||||||
|
this.writePermissionValue = writePermissionValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPlugin setInitialContextFactory(String initialContextFactory) {
|
||||||
|
this.initialContextFactory = initialContextFactory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void open() throws NamingException {
|
||||||
|
if (context != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hashtable<String, String> env = new Hashtable<String, String>();
|
||||||
|
env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
|
||||||
|
if (connectionUsername != null && !"".equals(connectionUsername)) {
|
||||||
|
env.put(Context.SECURITY_PRINCIPAL, connectionUsername);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new NamingException("Empty username is not allowed");
|
||||||
|
}
|
||||||
|
if (connectionPassword != null && !"".equals(connectionPassword)) {
|
||||||
|
env.put(Context.SECURITY_CREDENTIALS, connectionPassword);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new NamingException("Empty password is not allowed");
|
||||||
|
}
|
||||||
|
env.put(Context.SECURITY_PROTOCOL, connectionProtocol);
|
||||||
|
env.put(Context.PROVIDER_URL, connectionURL);
|
||||||
|
env.put(Context.SECURITY_AUTHENTICATION, authentication);
|
||||||
|
context = new InitialDirContext(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Set<Role>> getSecurityRoles() {
|
||||||
|
return securityRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LegacyLDAPSecuritySettingPlugin populateSecurityRoles() {
|
||||||
|
ActiveMQServerLogger.LOGGER.populatingSecurityRolesFromLDAP(connectionURL);
|
||||||
|
try {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
ActiveMQServerLogger.LOGGER.errorOpeningContextForLDAP(e);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchControls searchControls = new SearchControls();
|
||||||
|
searchControls.setReturningAttributes(new String[]{roleAttribute});
|
||||||
|
searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||||
|
|
||||||
|
Map<String, Set<Role>> securityRoles = new HashMap<String, Set<Role>>();
|
||||||
|
try {
|
||||||
|
NamingEnumeration<SearchResult> searchResults = context.search(destinationBase, filter, searchControls);
|
||||||
|
int i = 0;
|
||||||
|
while (searchResults.hasMore()) {
|
||||||
|
SearchResult searchResult = searchResults.next();
|
||||||
|
Attributes attrs = searchResult.getAttributes();
|
||||||
|
if (attrs == null || attrs.size() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LdapName searchResultLdapName = new LdapName(searchResult.getName());
|
||||||
|
ActiveMQServerLogger.LOGGER.debug("LDAP search result " + ++i + ": " + searchResultLdapName);
|
||||||
|
String permissionType = null;
|
||||||
|
String destination = null;
|
||||||
|
String destinationType = "unknown";
|
||||||
|
for (Rdn rdn : searchResultLdapName.getRdns()) {
|
||||||
|
if (rdn.getType().equals("cn")) {
|
||||||
|
ActiveMQServerLogger.LOGGER.debug("\tPermission type: " + rdn.getValue());
|
||||||
|
permissionType = rdn.getValue().toString();
|
||||||
|
}
|
||||||
|
if (rdn.getType().equals("uid")) {
|
||||||
|
ActiveMQServerLogger.LOGGER.debug("\tDestination name: " + rdn.getValue());
|
||||||
|
destination = rdn.getValue().toString();
|
||||||
|
}
|
||||||
|
if (rdn.getType().equals("ou")) {
|
||||||
|
String rawDestinationType = rdn.getValue().toString();
|
||||||
|
if (rawDestinationType.toLowerCase().contains("queue")) {
|
||||||
|
destinationType = "queue";
|
||||||
|
}
|
||||||
|
else if (rawDestinationType.toLowerCase().contains("topic")) {
|
||||||
|
destinationType = "topic";
|
||||||
|
}
|
||||||
|
ActiveMQServerLogger.LOGGER.debug("\tDestination type: " + destinationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ActiveMQServerLogger.LOGGER.debug("\tAttributes: " + attrs);
|
||||||
|
Attribute attr = attrs.get(roleAttribute);
|
||||||
|
NamingEnumeration<?> e = attr.getAll();
|
||||||
|
Set<Role> roles = securityRoles.get(destination);
|
||||||
|
boolean exists = false;
|
||||||
|
if (roles == null) {
|
||||||
|
roles = new HashSet<>();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
exists = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (e.hasMore()) {
|
||||||
|
String value = (String) e.next();
|
||||||
|
LdapName ldapname = new LdapName(value);
|
||||||
|
Rdn rdn = ldapname.getRdn(ldapname.size() - 1);
|
||||||
|
String roleName = rdn.getValue().toString();
|
||||||
|
ActiveMQServerLogger.LOGGER.debug("\tRole name: " + roleName);
|
||||||
|
Role role = new Role(roleName,
|
||||||
|
permissionType.equalsIgnoreCase(writePermissionValue),
|
||||||
|
permissionType.equalsIgnoreCase(readPermissionValue),
|
||||||
|
permissionType.equalsIgnoreCase(adminPermissionValue),
|
||||||
|
permissionType.equalsIgnoreCase(adminPermissionValue),
|
||||||
|
permissionType.equalsIgnoreCase(adminPermissionValue),
|
||||||
|
permissionType.equalsIgnoreCase(adminPermissionValue),
|
||||||
|
false); // there is no permission from ActiveMQ 5.x that corresponds to the "manage" permission in ActiveMQ Artemis
|
||||||
|
roles.add(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
securityRoles.put(destination, roles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
ActiveMQServerLogger.LOGGER.errorPopulatingSecurityRolesFromLDAP(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.securityRoles = securityRoles;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -711,6 +711,42 @@
|
||||||
</xsd:attribute>
|
</xsd:attribute>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
|
<xsd:element name="security-setting-plugin" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
a plugin
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="setting" maxOccurs="unbounded" minOccurs="0">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
the name of the setting
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
<xsd:attribute name="value" type="xsd:string" use="required">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
the value for the setting
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="class-name" type="xsd:string" use="required">
|
||||||
|
<xsd:annotation>
|
||||||
|
<xsd:documentation>
|
||||||
|
the name of the plugin class to instantiate
|
||||||
|
</xsd:documentation>
|
||||||
|
</xsd:annotation>
|
||||||
|
</xsd:attribute>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration;
|
import org.apache.activemq.artemis.api.core.BroadcastGroupConfiguration;
|
||||||
import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration;
|
import org.apache.activemq.artemis.api.core.DiscoveryGroupConfiguration;
|
||||||
|
@ -39,7 +40,9 @@ import org.apache.activemq.artemis.core.config.ha.LiveOnlyPolicyConfiguration;
|
||||||
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
|
import org.apache.activemq.artemis.core.remoting.impl.netty.TransportConstants;
|
||||||
import org.apache.activemq.artemis.core.security.Role;
|
import org.apache.activemq.artemis.core.security.Role;
|
||||||
import org.apache.activemq.artemis.core.server.JournalType;
|
import org.apache.activemq.artemis.core.server.JournalType;
|
||||||
|
import org.apache.activemq.artemis.core.server.SecuritySettingPlugin;
|
||||||
import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType;
|
import org.apache.activemq.artemis.core.server.cluster.impl.MessageLoadBalancingType;
|
||||||
|
import org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin;
|
||||||
import org.apache.activemq.artemis.core.settings.impl.SlowConsumerPolicy;
|
import org.apache.activemq.artemis.core.settings.impl.SlowConsumerPolicy;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -339,6 +342,22 @@ public class FileConfigurationTest extends ConfigurationImplTest {
|
||||||
assertTrue(a2Role.isDeleteNonDurableQueue());
|
assertTrue(a2Role.isDeleteNonDurableQueue());
|
||||||
assertFalse(a2Role.isManage());
|
assertFalse(a2Role.isManage());
|
||||||
|
|
||||||
|
List<SecuritySettingPlugin> securitySettingPlugins = conf.getSecuritySettingPlugins();
|
||||||
|
SecuritySettingPlugin securitySettingPlugin = securitySettingPlugins.get(0);
|
||||||
|
assertTrue(securitySettingPlugin instanceof LegacyLDAPSecuritySettingPlugin);
|
||||||
|
LegacyLDAPSecuritySettingPlugin legacyLDAPSecuritySettingPlugin = (LegacyLDAPSecuritySettingPlugin) securitySettingPlugin;
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getInitialContextFactory(), "testInitialContextFactory");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getConnectionURL(), "testConnectionURL");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getConnectionUsername(), "testConnectionUsername");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getConnectionPassword(), "testConnectionPassword");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getConnectionProtocol(), "testConnectionProtocol");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getAuthentication(), "testAuthentication");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getDestinationBase(), "testDestinationBase");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getFilter(), "testFilter");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getRoleAttribute(), "testRoleAttribute");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getAdminPermissionValue(), "testAdminPermissionValue");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getReadPermissionValue(), "testReadPermissionValue");
|
||||||
|
assertEquals(legacyLDAPSecuritySettingPlugin.getWritePermissionValue(), "testWritePermissionValue");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -232,6 +232,20 @@
|
||||||
<security-setting match="a2">
|
<security-setting match="a2">
|
||||||
<permission type="deleteNonDurableQueue" roles="a2.1"/>
|
<permission type="deleteNonDurableQueue" roles="a2.1"/>
|
||||||
</security-setting>
|
</security-setting>
|
||||||
|
<security-setting-plugin class-name="org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin">
|
||||||
|
<setting name="initialContextFactory" value="testInitialContextFactory"/>
|
||||||
|
<setting name="connectionURL" value="testConnectionURL"/>
|
||||||
|
<setting name="connectionUsername" value="testConnectionUsername"/>
|
||||||
|
<setting name="connectionPassword" value="testConnectionPassword"/>
|
||||||
|
<setting name="connectionProtocol" value="testConnectionProtocol"/>
|
||||||
|
<setting name="authentication" value="testAuthentication"/>
|
||||||
|
<setting name="destinationBase" value="testDestinationBase"/>
|
||||||
|
<setting name="filter" value="testFilter"/>
|
||||||
|
<setting name="roleAttribute" value="testRoleAttribute"/>
|
||||||
|
<setting name="adminPermissionValue" value="testAdminPermissionValue"/>
|
||||||
|
<setting name="readPermissionValue" value="testReadPermissionValue"/>
|
||||||
|
<setting name="writePermissionValue" value="testWritePermissionValue"/>
|
||||||
|
</security-setting-plugin>
|
||||||
</security-settings>
|
</security-settings>
|
||||||
|
|
||||||
<address-settings>
|
<address-settings>
|
||||||
|
|
|
@ -120,6 +120,121 @@ permissions in more specific security-setting blocks by simply not
|
||||||
specifying them. Otherwise it would not be possible to deny permissions
|
specifying them. Otherwise it would not be possible to deny permissions
|
||||||
in sub-groups of addresses.
|
in sub-groups of addresses.
|
||||||
|
|
||||||
|
## Security Setting Plugin
|
||||||
|
|
||||||
|
Aside from configuring sets of permissions via XML these permissions can also be
|
||||||
|
configured via plugins which implement `org.apache.activemq.artemis.core.server.SecuritySettingPlugin`.
|
||||||
|
One or more plugins can be defined and configured alongside the normal XML, e.g.:
|
||||||
|
|
||||||
|
<security-settings>
|
||||||
|
...
|
||||||
|
<security-setting-plugin class-name="org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin">
|
||||||
|
<setting name="initialContextFactory" value="com.sun.jndi.ldap.LdapCtxFactory"/>
|
||||||
|
<setting name="connectionURL" value="ldap://localhost:1024"/>
|
||||||
|
<setting name="connectionUsername" value="uid=admin,ou=system"/>
|
||||||
|
<setting name="connectionPassword" value="secret"/>
|
||||||
|
<setting name="connectionProtocol" value="s"/>
|
||||||
|
<setting name="authentication" value="simple"/>
|
||||||
|
</security-setting-plugin>
|
||||||
|
</security-settings>
|
||||||
|
|
||||||
|
Most of this configuration is specific to the plugin implementation. However, there are two configuration details that
|
||||||
|
will be specified for every implementation:
|
||||||
|
|
||||||
|
- `class-name`. This attribute of `security-setting-plugin` indicates the name of the class which implements
|
||||||
|
`org.apache.activemq.artemis.core.server.SecuritySettingPlugin`.
|
||||||
|
|
||||||
|
- `setting`. Each of these elements represents a name/value pair that will be passed to the implementation for configuration
|
||||||
|
purposes.
|
||||||
|
|
||||||
|
See the JavaDoc on `org.apache.activemq.artemis.core.server.SecuritySettingPlugin` for further details about the interface
|
||||||
|
and what each method is expected to do.
|
||||||
|
|
||||||
|
### Available plugins
|
||||||
|
|
||||||
|
#### LegacyLDAPSecuritySettingPlugin
|
||||||
|
|
||||||
|
This plugin will read the security information that was previously handled by [`LDAPAuthorizationMap`](http://activemq.apache.org/security.html)
|
||||||
|
and the [`cachedLDAPAuthorizationMap`](http://activemq.apache.org/cached-ldap-authorization-module.html) in Apache ActiveMQ 5.x
|
||||||
|
and turn it into Artemis security settings where possible. The security implementations of ActiveMQ 5.x and Artemis don't
|
||||||
|
match perfectly so some translation must occur to achieve near equivalent functionality.
|
||||||
|
|
||||||
|
Here is an example of the plugin's configuration:
|
||||||
|
|
||||||
|
<security-setting-plugin class-name="org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin">
|
||||||
|
<setting name="initialContextFactory" value="com.sun.jndi.ldap.LdapCtxFactory"/>
|
||||||
|
<setting name="connectionURL" value="ldap://localhost:1024"/>
|
||||||
|
<setting name="connectionUsername" value="uid=admin,ou=system"/>
|
||||||
|
<setting name="connectionPassword" value="secret"/>
|
||||||
|
<setting name="connectionProtocol" value="s"/>
|
||||||
|
<setting name="authentication" value="simple"/>
|
||||||
|
</security-setting-plugin>
|
||||||
|
|
||||||
|
- `class-name`. The implementation is `org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin`.
|
||||||
|
|
||||||
|
- `initialContextFactory`. The initial context factory used to connect to LDAP. It must always be set to
|
||||||
|
`com.sun.jndi.ldap.LdapCtxFactory` (i.e. the default value).
|
||||||
|
|
||||||
|
- `connectionURL`. Specifies the location of the directory server using an ldap URL, `ldap://Host:Port`. You can
|
||||||
|
optionally qualify this URL, by adding a forward slash, `/`, followed by the DN of a particular node in the directory
|
||||||
|
tree. For example, `ldap://ldapserver:10389/ou=system`. The default is `ldap://localhost:1024`.
|
||||||
|
|
||||||
|
- `connectionUsername`. The DN of the user that opens the connection to the directory server. For example, `uid=admin,ou=system`.
|
||||||
|
Directory servers generally require clients to present username/password credentials in order to open a connection.
|
||||||
|
|
||||||
|
- `connectionPassword`. The password that matches the DN from `connectionUsername`. In the directory server, in the
|
||||||
|
DIT, the password is normally stored as a `userPassword` attribute in the corresponding directory entry.
|
||||||
|
|
||||||
|
- `connectionProtocol`. Currently the only supported value is a blank string. In future, this option will allow you to
|
||||||
|
select the Secure Socket Layer (SSL) for the connection to the directory server. Note: this option must be set
|
||||||
|
explicitly to an empty string, because it has no default value.
|
||||||
|
|
||||||
|
- `authentication`. Specifies the authentication method used when binding to the LDAP server. Can take either of the
|
||||||
|
values, `simple` (username and password, the default value) or `none` (anonymous). Note: Simple Authentication and
|
||||||
|
Security Layer (SASL) authentication is currently not supported.
|
||||||
|
|
||||||
|
- `destinationBase`. Specifies the DN of the node whose children provide the permissions for all destinations. In this
|
||||||
|
case the DN is a literal value (that is, no string substitution is performed on the property value). For example, a
|
||||||
|
typical value of this property is `ou=destinations,o=ActiveMQ,ou=system` (i.e. the default value).
|
||||||
|
|
||||||
|
- `filter`. Specifies an LDAP search filter, which is used when looking up the permissions for any kind of destination.
|
||||||
|
The search filter attempts to match one of the children or descendants of the queue or topic node. The default value
|
||||||
|
is `(cn=*)`.
|
||||||
|
|
||||||
|
- `roleAttribute`. Specifies an attribute of the node matched by `filter`, whose value is the DN of a role. Default
|
||||||
|
value is `uniqueMember`.
|
||||||
|
|
||||||
|
- `adminPermissionValue`. Specifies a value that matches the `admin` permission. The default value is `admin`.
|
||||||
|
|
||||||
|
- `readPermissionValue`. Specifies a value that matches the `read` permission. The default value is `read`.
|
||||||
|
|
||||||
|
- `writePermissionValue`. Specifies a value that matches the `write` permission. The default value is `write`.
|
||||||
|
|
||||||
|
The name of the queue or topic defined in LDAP will serve as the "match" for the security-setting, the permission value
|
||||||
|
will be mapped from the ActiveMQ 5.x type to the Artemis type, and the role will be mapped as-is. It's worth noting that
|
||||||
|
since the name of queue or topic coming from LDAP will server as the "match" for the security-setting the security-setting
|
||||||
|
may not be applied as expected to JMS destinations since Artemis always prefixes JMS destinations with "jms.queue." or
|
||||||
|
"jms.topic." as necessary.
|
||||||
|
|
||||||
|
ActiveMQ 5.x only has 3 permission types - `read`, `write`, and `admin`. These permission types are described on their
|
||||||
|
[website](http://activemq.apache.org/security.html). However, as described previously, ActiveMQ Artemis has 6 permission
|
||||||
|
types - `createDurableQueue`, `deleteDurableQueue`, `createNonDurableQueue`, `deleteNonDurableQueue`, `send`, `consume`,
|
||||||
|
and `manage`. Here's how the old types are mapped to the new types:
|
||||||
|
|
||||||
|
- `read` - `consume`
|
||||||
|
- `write` - `send`
|
||||||
|
- `admin` - `createDurableQueue`, `deleteDurableQueue`, `createNonDurableQueue`, `deleteNonDurableQueue`
|
||||||
|
|
||||||
|
As mentioned, there are a few places where a translation was performed to achieve some equivalence.:
|
||||||
|
|
||||||
|
- This mapping doesn't include the Artemis `manage` permission type since there is no type analogous for that in ActiveMQ
|
||||||
|
5.x.
|
||||||
|
|
||||||
|
- The `admin` permission in ActiveMQ 5.x relates to whether or not the broker will auto-create a destination if
|
||||||
|
it doesn't exist and the user sends a message to it. Artemis automatically allows the automatic creation of a
|
||||||
|
destination if the user has permission to send message to it. Therefore, the plugin will map the `admin` permission
|
||||||
|
to the 4 aforementioned permissions in Artemis.
|
||||||
|
|
||||||
## Secure Sockets Layer (SSL) Transport
|
## Secure Sockets Layer (SSL) Transport
|
||||||
|
|
||||||
When messaging clients are connected to servers, or servers are
|
When messaging clients are connected to servers, or servers are
|
||||||
|
|
|
@ -0,0 +1,317 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.activemq.artemis.tests.integration.security;
|
||||||
|
|
||||||
|
import javax.naming.Context;
|
||||||
|
import javax.naming.NameClassPair;
|
||||||
|
import javax.naming.NamingEnumeration;
|
||||||
|
import javax.naming.directory.DirContext;
|
||||||
|
import javax.naming.directory.InitialDirContext;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.management.ManagementFactory;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
|
||||||
|
import org.apache.activemq.artemis.api.core.ActiveMQException;
|
||||||
|
import org.apache.activemq.artemis.api.core.SimpleString;
|
||||||
|
import org.apache.activemq.artemis.api.core.TransportConfiguration;
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientConsumer;
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientProducer;
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientSession;
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
|
||||||
|
import org.apache.activemq.artemis.api.core.client.ServerLocator;
|
||||||
|
import org.apache.activemq.artemis.core.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.remoting.impl.invm.InVMConnectorFactory;
|
||||||
|
import org.apache.activemq.artemis.core.server.ActiveMQServer;
|
||||||
|
import org.apache.activemq.artemis.core.server.ActiveMQServers;
|
||||||
|
import org.apache.activemq.artemis.core.server.impl.LegacyLDAPSecuritySettingPlugin;
|
||||||
|
import org.apache.activemq.artemis.spi.core.security.ActiveMQJAASSecurityManager;
|
||||||
|
import org.apache.activemq.artemis.tests.util.ActiveMQTestBase;
|
||||||
|
import org.apache.directory.server.annotations.CreateLdapServer;
|
||||||
|
import org.apache.directory.server.annotations.CreateTransport;
|
||||||
|
import org.apache.directory.server.core.annotations.ApplyLdifFiles;
|
||||||
|
import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
|
||||||
|
import org.apache.directory.server.core.integ.FrameworkRunner;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(FrameworkRunner.class)
|
||||||
|
@CreateLdapServer(transports = {@CreateTransport(protocol = "LDAP", port = 1024)})
|
||||||
|
@ApplyLdifFiles("AMQauth.ldif")
|
||||||
|
public class LegacyLDAPSecuritySettingPluginTest extends AbstractLdapTestUnit {
|
||||||
|
|
||||||
|
static {
|
||||||
|
String path = System.getProperty("java.security.auth.login.config");
|
||||||
|
if (path == null) {
|
||||||
|
URL resource = LegacyLDAPSecuritySettingPluginTest.class.getClassLoader().getResource("login.config");
|
||||||
|
if (resource != null) {
|
||||||
|
path = resource.getFile();
|
||||||
|
System.setProperty("java.security.auth.login.config", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServerLocator locator;
|
||||||
|
|
||||||
|
public static final String TARGET_TMP = "./target/tmp";
|
||||||
|
private static final String PRINCIPAL = "uid=admin,ou=system";
|
||||||
|
private static final String CREDENTIALS = "secret";
|
||||||
|
|
||||||
|
|
||||||
|
public LegacyLDAPSecuritySettingPluginTest() {
|
||||||
|
File parent = new File(TARGET_TMP);
|
||||||
|
parent.mkdirs();
|
||||||
|
temporaryFolder = new TemporaryFolder(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public TemporaryFolder temporaryFolder;
|
||||||
|
private String testDir;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
locator = ActiveMQClient.createServerLocatorWithHA(new TransportConfiguration(InVMConnectorFactory.class.getCanonicalName()));
|
||||||
|
testDir = temporaryFolder.getRoot().getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Test
|
||||||
|
public void testRunning() throws Exception {
|
||||||
|
Hashtable env = new Hashtable();
|
||||||
|
env.put(Context.PROVIDER_URL, "ldap://localhost:1024");
|
||||||
|
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
|
||||||
|
env.put(Context.SECURITY_AUTHENTICATION, "simple");
|
||||||
|
env.put(Context.SECURITY_PRINCIPAL, PRINCIPAL);
|
||||||
|
env.put(Context.SECURITY_CREDENTIALS, CREDENTIALS);
|
||||||
|
DirContext ctx = new InitialDirContext(env);
|
||||||
|
|
||||||
|
HashSet set = new HashSet();
|
||||||
|
|
||||||
|
NamingEnumeration list = ctx.list("ou=system");
|
||||||
|
|
||||||
|
while (list.hasMore()) {
|
||||||
|
NameClassPair ncp = (NameClassPair) list.next();
|
||||||
|
set.add(ncp.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.assertTrue(set.contains("uid=admin"));
|
||||||
|
Assert.assertTrue(set.contains("ou=users"));
|
||||||
|
Assert.assertTrue(set.contains("ou=groups"));
|
||||||
|
Assert.assertTrue(set.contains("ou=configuration"));
|
||||||
|
Assert.assertTrue(set.contains("prefNodeName=sysPrefRoot"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicPluginAuthorization() throws Exception {
|
||||||
|
ActiveMQServer server = getActiveMQServer();
|
||||||
|
server.start();
|
||||||
|
ClientSessionFactory cf = locator.createSessionFactory();
|
||||||
|
String name = "queue1";
|
||||||
|
|
||||||
|
try {
|
||||||
|
ClientSession session = cf.createSession("first", "secret", false, true, true, false, 0);
|
||||||
|
session.createQueue(SimpleString.toSimpleString(name), SimpleString.toSimpleString(name));
|
||||||
|
ClientProducer producer = session.createProducer();
|
||||||
|
producer.send(name, session.createMessage(false));
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Assert.fail("should not throw exception");
|
||||||
|
}
|
||||||
|
|
||||||
|
cf.close();
|
||||||
|
locator.close();
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPluginAuthorizationNegative() throws Exception {
|
||||||
|
final SimpleString ADDRESS = new SimpleString("queue2");
|
||||||
|
final SimpleString QUEUE = new SimpleString("queue2");
|
||||||
|
|
||||||
|
ActiveMQServer server = getActiveMQServer();
|
||||||
|
server.start();
|
||||||
|
server.createQueue(ADDRESS, QUEUE, null, true, false);
|
||||||
|
|
||||||
|
ClientSessionFactory cf = locator.createSessionFactory();
|
||||||
|
ClientSession session = cf.createSession("second", "secret", false, true, true, false, 0);
|
||||||
|
|
||||||
|
// CREATE_DURABLE_QUEUE
|
||||||
|
try {
|
||||||
|
session.createQueue(ADDRESS, QUEUE, true);
|
||||||
|
Assert.fail("should throw exception here");
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE_DURABLE_QUEUE
|
||||||
|
try {
|
||||||
|
session.deleteQueue(QUEUE);
|
||||||
|
Assert.fail("should throw exception here");
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// CREATE_NON_DURABLE_QUEUE
|
||||||
|
try {
|
||||||
|
session.createQueue(ADDRESS, QUEUE, false);
|
||||||
|
Assert.fail("should throw exception here");
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE_NON_DURABLE_QUEUE
|
||||||
|
try {
|
||||||
|
session.deleteQueue(QUEUE);
|
||||||
|
Assert.fail("should throw exception here");
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRODUCE
|
||||||
|
try {
|
||||||
|
ClientProducer producer = session.createProducer(ADDRESS);
|
||||||
|
producer.send(session.createMessage(true));
|
||||||
|
Assert.fail("should throw exception here");
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONSUME
|
||||||
|
try {
|
||||||
|
ClientConsumer consumer = session.createConsumer(QUEUE);
|
||||||
|
Assert.fail("should throw exception here");
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
cf.close();
|
||||||
|
locator.close();
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPluginAuthorizationPositive() throws Exception {
|
||||||
|
final SimpleString ADDRESS = new SimpleString("queue1");
|
||||||
|
final SimpleString QUEUE = new SimpleString("queue1");
|
||||||
|
|
||||||
|
ActiveMQServer server = getActiveMQServer();
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
ClientSessionFactory cf = locator.createSessionFactory();
|
||||||
|
ClientSession session = cf.createSession("first", "secret", false, true, true, false, 0);
|
||||||
|
|
||||||
|
// CREATE_DURABLE_QUEUE
|
||||||
|
try {
|
||||||
|
session.createQueue(ADDRESS, QUEUE, true);
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Assert.fail("should not throw exception here");
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE_DURABLE_QUEUE
|
||||||
|
try {
|
||||||
|
session.deleteQueue(QUEUE);
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Assert.fail("should not throw exception here");
|
||||||
|
}
|
||||||
|
|
||||||
|
// CREATE_NON_DURABLE_QUEUE
|
||||||
|
try {
|
||||||
|
session.createQueue(ADDRESS, QUEUE, false);
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
Assert.fail("should not throw exception here");
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE_NON_DURABLE_QUEUE
|
||||||
|
try {
|
||||||
|
session.deleteQueue(QUEUE);
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
Assert.fail("should not throw exception here");
|
||||||
|
}
|
||||||
|
|
||||||
|
session.createQueue(ADDRESS, QUEUE, true);
|
||||||
|
|
||||||
|
// PRODUCE
|
||||||
|
try {
|
||||||
|
ClientProducer producer = session.createProducer(ADDRESS);
|
||||||
|
producer.send(session.createMessage(true));
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
Assert.fail("should not throw exception here");
|
||||||
|
}
|
||||||
|
|
||||||
|
// CONSUME
|
||||||
|
try {
|
||||||
|
session.createConsumer(QUEUE);
|
||||||
|
}
|
||||||
|
catch (ActiveMQException e) {
|
||||||
|
Assert.fail("should not throw exception here");
|
||||||
|
}
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
cf.close();
|
||||||
|
locator.close();
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActiveMQServer getActiveMQServer() {
|
||||||
|
LegacyLDAPSecuritySettingPlugin legacyLDAPSecuritySettingPlugin = new LegacyLDAPSecuritySettingPlugin()
|
||||||
|
.setInitialContextFactory("com.sun.jndi.ldap.LdapCtxFactory")
|
||||||
|
.setConnectionURL("ldap://localhost:1024")
|
||||||
|
.setConnectionUsername("uid=admin,ou=system")
|
||||||
|
.setConnectionPassword("secret")
|
||||||
|
.setConnectionProtocol("s")
|
||||||
|
.setAuthentication("simple")
|
||||||
|
.populateSecurityRoles();
|
||||||
|
|
||||||
|
ActiveMQJAASSecurityManager securityManager = new ActiveMQJAASSecurityManager();
|
||||||
|
securityManager.setConfigurationName("LDAPLogin");
|
||||||
|
Configuration configuration = new ConfigurationImpl()
|
||||||
|
.setSecurityEnabled(true)
|
||||||
|
.addAcceptorConfiguration(new TransportConfiguration(InVMAcceptorFactory.class.getCanonicalName()))
|
||||||
|
.setJournalDirectory(ActiveMQTestBase.getJournalDir(testDir, 0, false))
|
||||||
|
.setBindingsDirectory(ActiveMQTestBase.getBindingsDir(testDir, 0, false))
|
||||||
|
.setPagingDirectory(ActiveMQTestBase.getPageDir(testDir, 0, false))
|
||||||
|
.setLargeMessagesDirectory(ActiveMQTestBase.getLargeMessagesDir(testDir, 0, false))
|
||||||
|
.setPersistenceEnabled(false)
|
||||||
|
.addSecuritySettingPlugin(legacyLDAPSecuritySettingPlugin);
|
||||||
|
|
||||||
|
return ActiveMQServers.newActiveMQServer(configuration, ManagementFactory.getPlatformMBeanServer(), securityManager, false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
## ---------------------------------------------------------------------------
|
||||||
|
## 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.
|
||||||
|
## ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
dn: o=ActiveMQ,ou=system
|
||||||
|
objectclass: organization
|
||||||
|
objectclass: top
|
||||||
|
o: ActiveMQ
|
||||||
|
|
||||||
|
dn: ou=users,o=ActiveMQ,ou=system
|
||||||
|
objectclass: organizationalUnit
|
||||||
|
objectclass: top
|
||||||
|
ou: users
|
||||||
|
|
||||||
|
dn: uid=first,ou=system
|
||||||
|
uid: first
|
||||||
|
userPassword: secret
|
||||||
|
objectClass: account
|
||||||
|
objectClass: simpleSecurityObject
|
||||||
|
objectClass: top
|
||||||
|
|
||||||
|
dn: uid=second,ou=system
|
||||||
|
uid: second
|
||||||
|
userPassword: secret
|
||||||
|
objectClass: account
|
||||||
|
objectClass: simpleSecurityObject
|
||||||
|
objectClass: top
|
||||||
|
|
||||||
|
dn: cn=role1,ou=system
|
||||||
|
cn: role1
|
||||||
|
member: uid=first,ou=system
|
||||||
|
objectClass: groupOfNames
|
||||||
|
objectClass: top
|
||||||
|
|
||||||
|
dn: cn=role2,ou=system
|
||||||
|
cn: role2
|
||||||
|
member: uid=second,ou=system
|
||||||
|
objectClass: groupOfNames
|
||||||
|
objectClass: top
|
||||||
|
|
||||||
|
dn: ou=destinations,o=ActiveMQ,ou=system
|
||||||
|
objectclass: organizationalUnit
|
||||||
|
objectclass: top
|
||||||
|
ou: destinations
|
||||||
|
|
||||||
|
dn: ou=queues,ou=destinations,o=ActiveMQ,ou=system
|
||||||
|
objectclass: organizationalUnit
|
||||||
|
objectclass: top
|
||||||
|
ou: queues
|
||||||
|
|
||||||
|
dn: uid=queue1,ou=queues,ou=destinations,o=ActiveMQ,ou=system
|
||||||
|
objectclass: applicationProcess
|
||||||
|
objectclass: uidObject
|
||||||
|
objectclass: top
|
||||||
|
uid: queue1
|
||||||
|
cn: queue1
|
||||||
|
|
||||||
|
dn: uid=queue2,ou=queues,ou=destinations,o=ActiveMQ,ou=system
|
||||||
|
objectclass: applicationProcess
|
||||||
|
objectclass: uidObject
|
||||||
|
objectclass: top
|
||||||
|
uid: queue2
|
||||||
|
cn: queue2
|
||||||
|
|
||||||
|
dn: cn=read,uid=queue1,ou=queues,ou=destinations,o=ActiveMQ,ou=system
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
objectclass: top
|
||||||
|
cn: read
|
||||||
|
uniquemember: uid=role1
|
||||||
|
|
||||||
|
dn: cn=write,uid=queue1,ou=queues,ou=destinations,o=ActiveMQ,ou=system
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
objectclass: top
|
||||||
|
cn: write
|
||||||
|
uniquemember: uid=role1
|
||||||
|
|
||||||
|
dn: cn=admin,uid=queue1,ou=queues,ou=destinations,o=ActiveMQ,ou=system
|
||||||
|
objectclass: groupOfUniqueNames
|
||||||
|
objectclass: top
|
||||||
|
cn: admin
|
||||||
|
uniquemember: uid=role1
|
Loading…
Reference in New Issue