summaryrefslogtreecommitdiff
path: root/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java')
-rw-r--r--qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java348
1 files changed, 297 insertions, 51 deletions
diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
index d36bbc4f46..1945c2e15f 100644
--- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
+++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/PrincipalDatabaseAuthenticationManager.java
@@ -20,32 +20,64 @@
*/
package org.apache.qpid.server.security.auth.manager;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import javax.security.auth.Subject;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.login.AccountNotFoundException;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslServerFactory;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
import org.apache.log4j.Logger;
-import org.apache.qpid.server.registry.ApplicationRegistry;
-import org.apache.qpid.server.security.auth.manager.AuthenticationManager;
+import org.apache.qpid.configuration.PropertyException;
+import org.apache.qpid.configuration.PropertyUtils;
+import org.apache.qpid.server.configuration.plugins.ConfigurationPlugin;
+import org.apache.qpid.server.configuration.plugins.ConfigurationPluginFactory;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus;
import org.apache.qpid.server.security.auth.database.PrincipalDatabase;
-import org.apache.qpid.server.security.auth.sasl.JCAProvider;
+import org.apache.qpid.server.security.auth.management.AMQUserManagementMBean;
import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser;
+import org.apache.qpid.server.security.auth.sasl.JCAProvider;
import org.apache.qpid.server.security.auth.sasl.UsernamePrincipal;
-import org.apache.qpid.server.security.auth.AuthenticationResult;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.login.AccountNotFoundException;
-import javax.security.sasl.SaslServerFactory;
-import javax.security.sasl.SaslServer;
-import javax.security.sasl.SaslException;
-import javax.security.sasl.Sasl;
-import java.util.Map;
-import java.util.HashMap;
-import java.util.TreeMap;
-import java.security.Security;
/**
* Concrete implementation of the AuthenticationManager that determines if supplied
- * user credentials match those appearing in a PrincipalDatabase.
+ * user credentials match those appearing in a PrincipalDatabase. The implementation
+ * of the PrincipalDatabase is determined from the configuration.
+ *
+ * This implementation also registers the JMX UserManagemement MBean.
+ *
+ * This plugin expects configuration such as:
*
+ * <pre>
+ * &lt;pd-auth-manager&gt;
+ * &lt;principal-database&gt;
+ * &lt;class&gt;org.apache.qpid.server.security.auth.database.PlainPasswordFilePrincipalDatabase&lt;/class&gt;
+ * &lt;attributes&gt;
+ * &lt;attribute&gt;
+ * &lt;name>passwordFile&lt;/name&gt;
+ * &lt;value>${conf}/passwd&lt;/value&gt;
+ * &lt;/attribute&gt;
+ * &lt;/attributes&gt;
+ * &lt;/principal-database&gt;
+ * &lt;/pd-auth-manager&gt;
+ * </pre>
*/
public class PrincipalDatabaseAuthenticationManager implements AuthenticationManager
{
@@ -55,25 +87,109 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan
private String _mechanisms;
/** Maps from the mechanism to the callback handler to use for handling those requests */
- private Map<String, CallbackHandler> _callbackHandlerMap = new HashMap<String, CallbackHandler>();
+ private final Map<String, CallbackHandler> _callbackHandlerMap = new HashMap<String, CallbackHandler>();
/**
* Maps from the mechanism to the properties used to initialise the server. See the method Sasl.createSaslServer for
* details of the use of these properties. This map is populated during initialisation of each provider.
*/
- private Map<String, Map<String, ?>> _serverCreationProperties = new HashMap<String, Map<String, ?>>();
+ private final Map<String, Map<String, ?>> _serverCreationProperties = new HashMap<String, Map<String, ?>>();
+
+ protected PrincipalDatabase _principalDatabase = null;
- /** The name for the required SASL Server mechanisms */
- public static final String PROVIDER_NAME= "AMQSASLProvider-Server";
+ protected AMQUserManagementMBean _mbean = null;
- public PrincipalDatabaseAuthenticationManager()
+ public static final AuthenticationManagerPluginFactory<PrincipalDatabaseAuthenticationManager> FACTORY = new AuthenticationManagerPluginFactory<PrincipalDatabaseAuthenticationManager>()
{
- _logger.info("Initialising PrincipalDatabase authentication manager.");
+ public PrincipalDatabaseAuthenticationManager newInstance(final ConfigurationPlugin config) throws ConfigurationException
+ {
+ final PrincipalDatabaseAuthenticationManagerConfiguration configuration = config.getConfiguration(PrincipalDatabaseAuthenticationManagerConfiguration.class.getName());
- Map<String, Class<? extends SaslServerFactory>> providerMap = new TreeMap<String, Class<? extends SaslServerFactory>>();
+ // If there is no configuration for this plugin then don't load it.
+ if (configuration == null)
+ {
+ _logger.info("No authentication-manager configuration found for PrincipalDatabaseAuthenticationManager");
+ return null;
+ }
+
+ final PrincipalDatabaseAuthenticationManager pdam = new PrincipalDatabaseAuthenticationManager();
+ pdam.configure(configuration);
+ pdam.initialise();
+ return pdam;
+ }
+ public Class<PrincipalDatabaseAuthenticationManager> getPluginClass()
+ {
+ return PrincipalDatabaseAuthenticationManager.class;
+ }
- initialiseAuthenticationMechanisms(providerMap, ApplicationRegistry.getInstance().getDatabaseManager().getDatabases());
+ public String getPluginName()
+ {
+ return PrincipalDatabaseAuthenticationManager.class.getName();
+ }
+ };
+
+ public static class PrincipalDatabaseAuthenticationManagerConfiguration extends ConfigurationPlugin {
+
+ public static final ConfigurationPluginFactory FACTORY = new ConfigurationPluginFactory()
+ {
+ public List<String> getParentPaths()
+ {
+ return Arrays.asList("security.pd-auth-manager");
+ }
+
+ public ConfigurationPlugin newInstance(final String path, final Configuration config) throws ConfigurationException
+ {
+ final ConfigurationPlugin instance = new PrincipalDatabaseAuthenticationManagerConfiguration();
+
+ instance.setConfiguration(path, config);
+ return instance;
+ }
+ };
+
+ public String[] getElementsProcessed()
+ {
+ return new String[] {"principal-database.class",
+ "principal-database.attributes.attribute.name",
+ "principal-database.attributes.attribute.value"};
+ }
+
+ public void validateConfiguration() throws ConfigurationException
+ {
+ }
+
+ public String getPrincipalDatabaseClass()
+ {
+ return _configuration.getString("principal-database.class");
+ }
+
+ public Map<String,String> getPdClassAttributeMap() throws ConfigurationException
+ {
+ final List<String> argumentNames = _configuration.getList("principal-database.attributes.attribute.name");
+ final List<String> argumentValues = _configuration.getList("principal-database.attributes.attribute.value");
+ final Map<String,String> attributes = new HashMap<String,String>(argumentNames.size());
+
+ for (int i = 0; i < argumentNames.size(); i++)
+ {
+ final String argName = argumentNames.get(i);
+ final String argValue = argumentValues.get(i);
+
+ attributes.put(argName, argValue);
+ }
+
+ return Collections.unmodifiableMap(attributes);
+ }
+ }
+
+ protected PrincipalDatabaseAuthenticationManager()
+ {
+ }
+
+ public void initialise()
+ {
+ final Map<String, Class<? extends SaslServerFactory>> providerMap = new TreeMap<String, Class<? extends SaslServerFactory>>();
+
+ initialiseAuthenticationMechanisms(providerMap, _principalDatabase);
if (providerMap.size() > 0)
{
@@ -86,28 +202,13 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan
{
_logger.info("Additional SASL providers successfully registered.");
}
-
}
else
{
_logger.warn("No additional SASL providers registered.");
}
- }
-
- private void initialiseAuthenticationMechanisms(Map<String, Class<? extends SaslServerFactory>> providerMap, Map<String, PrincipalDatabase> databases)
- {
- if (databases.size() > 1)
- {
- _logger.warn("More than one principle database provided currently authentication mechanism will override each other.");
- }
- for (Map.Entry<String, PrincipalDatabase> entry : databases.entrySet())
- {
- // fixme As the database now provide the mechanisms they support, they will ...
- // overwrite each other in the map. There should only be one database per vhost.
- // But currently we must have authentication before vhost definition.
- initialiseAuthenticationMechanisms(providerMap, entry.getValue());
- }
+ registerManagement();
}
private void initialiseAuthenticationMechanisms(Map<String, Class<? extends SaslServerFactory>> providerMap, PrincipalDatabase database)
@@ -126,7 +227,6 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan
private void initialiseAuthenticationMechanism(String mechanism, AuthenticationProviderInitialiser initialiser,
Map<String, Class<? extends SaslServerFactory>> providerMap)
-
{
if (_mechanisms == null)
{
@@ -147,6 +247,21 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan
_logger.info("Initialised " + mechanism + " SASL provider successfully");
}
+ /**
+ * @see org.apache.qpid.server.plugins.Plugin#configure(org.apache.qpid.server.configuration.plugins.ConfigurationPlugin)
+ */
+ public void configure(final ConfigurationPlugin config) throws ConfigurationException
+ {
+ final PrincipalDatabaseAuthenticationManagerConfiguration pdamConfig = (PrincipalDatabaseAuthenticationManagerConfiguration) config;
+ final String pdClazz = pdamConfig.getPrincipalDatabaseClass();
+
+ _logger.info("PrincipalDatabase concrete implementation : " + pdClazz);
+
+ _principalDatabase = createPrincipalDatabaseImpl(pdClazz);
+
+ configPrincipalDatabase(_principalDatabase, pdamConfig);
+ }
+
public String getMechanisms()
{
return _mechanisms;
@@ -158,6 +273,9 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan
_callbackHandlerMap.get(mechanism));
}
+ /**
+ * @see org.apache.qpid.server.security.auth.manager.AuthenticationManager#authenticate(SaslServer, byte[])
+ */
public AuthenticationResult authenticate(SaslServer server, byte[] response)
{
try
@@ -182,23 +300,14 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan
}
}
- public void close()
- {
- _mechanisms = null;
- Security.removeProvider(PROVIDER_NAME);
- }
-
/**
* @see org.apache.qpid.server.security.auth.manager.AuthenticationManager#authenticate(String, String)
*/
- @Override
public AuthenticationResult authenticate(final String username, final String password)
{
- final PrincipalDatabase db = ApplicationRegistry.getInstance().getDatabaseManager().getDatabases().values().iterator().next();
-
try
{
- if (db.verifyPassword(username, password.toCharArray()))
+ if (_principalDatabase.verifyPassword(username, password.toCharArray()))
{
final Subject subject = new Subject();
subject.getPrincipals().add(new UsernamePrincipal(username));
@@ -214,4 +323,141 @@ public class PrincipalDatabaseAuthenticationManager implements AuthenticationMan
return new AuthenticationResult(AuthenticationStatus.CONTINUE);
}
}
+
+ public void close()
+ {
+ _mechanisms = null;
+ Security.removeProvider(PROVIDER_NAME);
+
+ unregisterManagement();
+ }
+
+ private PrincipalDatabase createPrincipalDatabaseImpl(final String pdClazz) throws ConfigurationException
+ {
+ try
+ {
+ return (PrincipalDatabase) Class.forName(pdClazz).newInstance();
+ }
+ catch (InstantiationException ie)
+ {
+ throw new ConfigurationException("Cannot instantiate " + pdClazz, ie);
+ }
+ catch (IllegalAccessException iae)
+ {
+ throw new ConfigurationException("Cannot access " + pdClazz, iae);
+ }
+ catch (ClassNotFoundException cnfe)
+ {
+ throw new ConfigurationException("Cannot load " + pdClazz + " implementation", cnfe);
+ }
+ catch (ClassCastException cce)
+ {
+ throw new ConfigurationException("Expecting a " + PrincipalDatabase.class + " implementation", cce);
+ }
+ }
+
+ private void configPrincipalDatabase(final PrincipalDatabase principalDatabase, final PrincipalDatabaseAuthenticationManagerConfiguration config)
+ throws ConfigurationException
+ {
+
+ final Map<String,String> attributes = config.getPdClassAttributeMap();
+
+ for (Iterator<Entry<String, String>> iterator = attributes.entrySet().iterator(); iterator.hasNext();)
+ {
+ final Entry<String, String> nameValuePair = iterator.next();
+ final String methodName = generateSetterName(nameValuePair.getKey());
+ final Method method;
+ try
+ {
+ method = principalDatabase.getClass().getMethod(methodName, String.class);
+ }
+ catch (Exception e)
+ {
+ throw new ConfigurationException("No method " + methodName + " found in class "
+ + principalDatabase.getClass()
+ + " hence unable to configure principal database. The method must be public and "
+ + "have a single String argument with a void return type", e);
+ }
+ try
+ {
+ method.invoke(principalDatabase, PropertyUtils.replaceProperties(nameValuePair.getValue()));
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new ConfigurationException(e.getMessage(), e);
+ }
+ catch (PropertyException e)
+ {
+ throw new ConfigurationException(e.getMessage(), e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ConfigurationException(e.getMessage(), e);
+ }
+ catch (InvocationTargetException e)
+ {
+ // QPID-1347.. InvocationTargetException wraps the checked exception thrown from the reflective
+ // method call. Pull out the underlying message and cause to make these more apparent to the user.
+ throw new ConfigurationException(e.getCause().getMessage(), e.getCause());
+ }
+ }
+ }
+
+ private String generateSetterName(String argName) throws ConfigurationException
+ {
+ if ((argName == null) || (argName.length() == 0))
+ {
+ throw new ConfigurationException("Argument names must have length >= 1 character");
+ }
+
+ if (Character.isLowerCase(argName.charAt(0)))
+ {
+ argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1);
+ }
+
+ final String methodName = "set" + argName;
+ return methodName;
+ }
+
+ protected void setPrincipalDatabase(final PrincipalDatabase principalDatabase)
+ {
+ _principalDatabase = principalDatabase;
+ }
+
+ protected void registerManagement()
+ {
+ try
+ {
+ _logger.info("Registering UserManagementMBean");
+
+ _mbean = new AMQUserManagementMBean();
+ _mbean.setPrincipalDatabase(_principalDatabase);
+ _mbean.register();
+ }
+ catch (Exception e)
+ {
+ _logger.warn("User management disabled as unable to create MBean:", e);
+ _mbean = null;
+ }
+ }
+
+ protected void unregisterManagement()
+ {
+ try
+ {
+ if (_mbean != null)
+ {
+ _logger.info("Unregistering UserManagementMBean");
+ _mbean.unregister();
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.warn("Failed to unregister User management MBean:", e);
+ }
+ finally
+ {
+ _mbean = null;
+ }
+ }
}