From 532bc2b78fb4a136fe035d68e3146ec7b2fa40aa Mon Sep 17 00:00:00 2001 From: Alex Rudyy Date: Wed, 17 Apr 2013 10:53:55 +0000 Subject: QPID-4746, QPID-4747: remove the defaultAuthenticationProvider attribute from broker and add an overriding authentication provider for management mode git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1468830 13f79535-47bb-0310-9956-ffa450edef68 --- .../main/java/org/apache/qpid/server/Broker.java | 2 +- .../java/org/apache/qpid/server/BrokerOptions.java | 19 ++ .../src/main/java/org/apache/qpid/server/Main.java | 9 + .../configuration/startup/BrokerRecoverer.java | 74 +----- .../startup/DefaultRecovererProvider.java | 7 +- .../store/ManagementModeStoreHandler.java | 5 + .../logging/messages/Broker_logmessages.properties | 4 +- .../java/org/apache/qpid/server/model/Broker.java | 6 +- .../java/org/apache/qpid/server/model/Port.java | 6 +- .../adapter/AuthenticationProviderAdapter.java | 4 - .../qpid/server/model/adapter/BrokerAdapter.java | 72 +++--- .../qpid/server/model/adapter/PortAdapter.java | 31 ++- .../qpid/server/registry/ApplicationRegistry.java | 5 +- .../qpid/server/registry/IApplicationRegistry.java | 9 +- .../auth/jmx/JMXPasswordAuthenticator.java | 136 +++++++++++ .../auth/manager/SimpleAuthenticationManager.java | 214 +++++++++++++++++ .../auth/rmi/RMIPasswordAuthenticator.java | 136 ----------- .../org/apache/qpid/server/util/StringUtil.java | 44 ++++ .../broker/src/main/resources/initial-config.json | 6 +- .../org/apache/qpid/server/BrokerOptionsTest.java | 6 + .../test/java/org/apache/qpid/server/MainTest.java | 22 ++ .../configuration/startup/BrokerRecovererTest.java | 91 +------- .../startup/DefaultRecovererProviderTest.java | 3 +- .../store/ConfigurationEntryStoreTestCase.java | 2 - .../qpid/server/model/BrokerShutdownTest.java | 3 +- .../qpid/server/model/adapter/PortFactoryTest.java | 11 +- .../auth/database/PropertiesPrincipalDatabase.java | 160 ------------- .../auth/jmx/JMXPasswordAuthenticatorTest.java | 259 +++++++++++++++++++++ .../manager/SimpleAuthenticationManagerTest.java | 140 +++++++++++ .../auth/rmi/RMIPasswordAuthenticatorTest.java | 258 -------------------- .../auth/sasl/CRAMMD5HexInitialiserTest.java | 109 +++++---- .../qpid/server/security/auth/sasl/SaslUtil.java | 85 +++++++ .../apache/qpid/server/util/StringUtilTest.java | 38 +++ 33 files changed, 1147 insertions(+), 829 deletions(-) create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticator.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java delete mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java create mode 100644 qpid/java/broker/src/main/java/org/apache/qpid/server/util/StringUtil.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticatorTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManagerTest.java delete mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslUtil.java create mode 100644 qpid/java/broker/src/test/java/org/apache/qpid/server/util/StringUtilTest.java (limited to 'qpid/java/broker/src') diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java index 5f0c7c7d3c..dd0fde5f7a 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Broker.java @@ -134,7 +134,7 @@ public class Broker _applicationRegistry = new ApplicationRegistry(store); try { - _applicationRegistry.initialise(); + _applicationRegistry.initialise(options); } catch(Exception e) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java index 8a8f7606a2..5c2a8fd090 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/BrokerOptions.java @@ -25,6 +25,7 @@ import java.io.File; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.store.MemoryConfigurationEntryStore; +import org.apache.qpid.server.util.StringUtil; public class BrokerOptions { @@ -34,6 +35,8 @@ public class BrokerOptions public static final String DEFAULT_LOG_CONFIG_FILE = "etc/log4j.xml"; public static final String DEFAULT_INITIAL_CONFIG_LOCATION = BrokerOptions.class.getClassLoader().getResource(DEFAULT_INITIAL_CONFIG_NAME).toExternalForm(); + public static final String MANAGEMENT_MODE_USER_NAME = "mm_admin"; + private static final int MANAGEMENT_MODE_PASSWORD_LENGTH = 10; private String _logConfigFile; private Integer _logWatchFrequency = 0; @@ -48,6 +51,7 @@ public class BrokerOptions private int _managementModeRmiPort; private int _managementModeConnectorPort; private int _managementModeHttpPort; + private String _managementModePassword; private String _workingDir; private boolean _skipLoggingConfiguration; private boolean _overwriteConfigurationStore; @@ -57,6 +61,21 @@ public class BrokerOptions return _logConfigFile; } + public String getManagementModePassword() + { + if(_managementModePassword == null) + { + _managementModePassword = new StringUtil().randomAlphaNumericString(MANAGEMENT_MODE_PASSWORD_LENGTH); + } + + return _managementModePassword; + } + + public void setManagementModePassword(String managementModePassword) + { + _managementModePassword = managementModePassword; + } + public void setLogConfigFile(final String logConfigFile) { _logConfigFile = logConfigFile; diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java index 28827a8bb5..46612613dd 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/Main.java @@ -81,6 +81,8 @@ public class Main .withDescription("override jmx connector port in management mode").withLongOpt("management-mode-jmx-connector-port").create("mmjmx"); private static final Option OPTION_MM_HTTP_PORT = OptionBuilder.withArgName("port").hasArg() .withDescription("override http management port in management mode").withLongOpt("management-mode-http-port").create("mmhttp"); + private static final Option OPTION_MM_PASSWORD = OptionBuilder.withArgName("password").hasArg() + .withDescription("Set the password for the management mode user " + BrokerOptions.MANAGEMENT_MODE_USER_NAME).withLongOpt("management-mode-password").create("mmpass"); private static final Options OPTIONS = new Options(); @@ -100,6 +102,7 @@ public class Main OPTIONS.addOption(OPTION_MM_RMI_PORT); OPTIONS.addOption(OPTION_MM_CONNECTOR_PORT); OPTIONS.addOption(OPTION_MM_HTTP_PORT); + OPTIONS.addOption(OPTION_MM_PASSWORD); } protected CommandLine _commandLine; @@ -256,6 +259,12 @@ public class Main boolean quiesceVhosts = _commandLine.hasOption(OPTION_MM_QUIESCE_VHOST.getOpt()); options.setManagementModeQuiesceVirtualHosts(quiesceVhosts); + + String password = _commandLine.getOptionValue(OPTION_MM_PASSWORD.getOpt()); + if (password != null) + { + options.setManagementModePassword(password); + } } setExceptionHandler(); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java index 7251abfab0..be75b4d2e9 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/BrokerRecoverer.java @@ -26,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; import org.apache.qpid.server.configuration.IllegalConfigurationException; @@ -55,10 +56,11 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer private final AuthenticationProviderFactory _authenticationProviderFactory; private final PortFactory _portFactory; private final TaskExecutor _taskExecutor; + private final BrokerOptions _brokerOptions; public BrokerRecoverer(AuthenticationProviderFactory authenticationProviderFactory, PortFactory portFactory, StatisticsGatherer statisticsGatherer, VirtualHostRegistry virtualHostRegistry, LogRecorder logRecorder, - RootMessageLogger rootMessageLogger, TaskExecutor taskExecutor) + RootMessageLogger rootMessageLogger, TaskExecutor taskExecutor, BrokerOptions brokerOptions) { _portFactory = portFactory; _authenticationProviderFactory = authenticationProviderFactory; @@ -67,6 +69,7 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer _logRecorder = logRecorder; _rootMessageLogger = rootMessageLogger; _taskExecutor = taskExecutor; + _brokerOptions = brokerOptions; } @Override @@ -74,38 +77,37 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer { StoreConfigurationChangeListener storeChangeListener = new StoreConfigurationChangeListener(entry.getStore()); BrokerAdapter broker = new BrokerAdapter(entry.getId(), entry.getAttributes(), _statisticsGatherer, _virtualHostRegistry, - _logRecorder, _rootMessageLogger, _authenticationProviderFactory, _portFactory, _taskExecutor, entry.getStore()); + _logRecorder, _rootMessageLogger, _authenticationProviderFactory, _portFactory, _taskExecutor, entry.getStore(), _brokerOptions); broker.addChangeListener(storeChangeListener); //Recover the SSL keystores / truststores first, then others that depend on them Map> childEntries = new HashMap>(entry.getChildren()); - Map> sslChildEntries = new HashMap>(childEntries); + Map> priorityChildEntries = new HashMap>(childEntries); List types = new ArrayList(childEntries.keySet()); for(String type : types) { - if(KeyStore.class.getSimpleName().equals(type) || TrustStore.class.getSimpleName().equals(type)) + if(KeyStore.class.getSimpleName().equals(type) || TrustStore.class.getSimpleName().equals(type) + || AuthenticationProvider.class.getSimpleName().equals(type)) { childEntries.remove(type); } else { - sslChildEntries.remove(type); + priorityChildEntries.remove(type); } } - for (String type : sslChildEntries.keySet()) + for (String type : priorityChildEntries.keySet()) { - recoverType(recovererProvider, storeChangeListener, broker, sslChildEntries, type); + recoverType(recovererProvider, storeChangeListener, broker, priorityChildEntries, type); } for (String type : childEntries.keySet()) { recoverType(recovererProvider, storeChangeListener, broker, childEntries, type); } - wireUpConfiguredObjects(broker, entry.getAttributes()); - return broker; } @@ -132,58 +134,4 @@ public class BrokerRecoverer implements ConfiguredObjectRecoverer object.addChangeListener(storeChangeListener); } } - - private void wireUpConfiguredObjects(BrokerAdapter broker, Map brokerAttributes) - { - AuthenticationProvider defaultAuthenticationProvider = null; - Collection authenticationProviders = broker.getAuthenticationProviders(); - int numberOfAuthenticationProviders = authenticationProviders.size(); - if (numberOfAuthenticationProviders == 0) - { - throw new IllegalConfigurationException("No authentication provider was configured"); - } - else if (numberOfAuthenticationProviders == 1) - { - defaultAuthenticationProvider = authenticationProviders.iterator().next(); - } - else - { - String name = (String) brokerAttributes.get(Broker.DEFAULT_AUTHENTICATION_PROVIDER); - if (name == null) - { - throw new IllegalConfigurationException("Multiple authentication providers defined, but no default was configured."); - } - - defaultAuthenticationProvider = getAuthenticationProviderByName(broker, name); - } - broker.setDefaultAuthenticationProvider(defaultAuthenticationProvider); - - Collection ports = broker.getPorts(); - for (Port port : ports) - { - String authenticationProviderName = (String) port.getAttribute(Port.AUTHENTICATION_PROVIDER); - AuthenticationProvider provider = null; - if (authenticationProviderName != null) - { - provider = getAuthenticationProviderByName(broker, authenticationProviderName); - } - else - { - provider = defaultAuthenticationProvider; - } - port.setAuthenticationProvider(provider); - } - } - - private AuthenticationProvider getAuthenticationProviderByName(BrokerAdapter broker, String authenticationProviderName) - { - AuthenticationProvider provider = broker.findAuthenticationProviderByName(authenticationProviderName); - if (provider == null) - { - throw new IllegalConfigurationException("Cannot find the authentication provider with name: " - + authenticationProviderName); - } - return provider; - } - } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java index 15cb229d8a..35209e5b41 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProvider.java @@ -20,6 +20,7 @@ */ package org.apache.qpid.server.configuration.startup; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; import org.apache.qpid.server.configuration.RecovererProvider; import org.apache.qpid.server.logging.LogRecorder; @@ -54,9 +55,10 @@ public class DefaultRecovererProvider implements RecovererProvider private final QpidServiceLoader _groupManagerServiceLoader; private final QpidServiceLoader _pluginFactoryServiceLoader; private final TaskExecutor _taskExecutor; + private final BrokerOptions _brokerOptions; public DefaultRecovererProvider(StatisticsGatherer brokerStatisticsGatherer, VirtualHostRegistry virtualHostRegistry, - LogRecorder logRecorder, RootMessageLogger rootMessageLogger, TaskExecutor taskExecutor) + LogRecorder logRecorder, RootMessageLogger rootMessageLogger, TaskExecutor taskExecutor, BrokerOptions brokerOptions) { _authenticationProviderFactory = new AuthenticationProviderFactory(new QpidServiceLoader()); _portFactory = new PortFactory(); @@ -67,6 +69,7 @@ public class DefaultRecovererProvider implements RecovererProvider _groupManagerServiceLoader = new QpidServiceLoader(); _pluginFactoryServiceLoader = new QpidServiceLoader(); _taskExecutor = taskExecutor; + _brokerOptions = brokerOptions; } @Override @@ -75,7 +78,7 @@ public class DefaultRecovererProvider implements RecovererProvider if (Broker.class.getSimpleName().equals(type)) { return new BrokerRecoverer(_authenticationProviderFactory, _portFactory, _brokerStatisticsGatherer, _virtualHostRegistry, - _logRecorder, _rootMessageLogger, _taskExecutor); + _logRecorder, _rootMessageLogger, _taskExecutor, _brokerOptions); } else if(VirtualHost.class.getSimpleName().equals(type)) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java index 6733c8ee2d..24d5edf00f 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/configuration/store/ManagementModeStoreHandler.java @@ -26,6 +26,7 @@ public class ManagementModeStoreHandler implements ConfigurationEntryStore private static final String PORT_TYPE = Port.class.getSimpleName(); private static final String VIRTUAL_HOST_TYPE = VirtualHost.class.getSimpleName(); private static final String ATTRIBUTE_STATE = VirtualHost.STATE; + private static final Object MANAGEMENT_MODE_AUTH_PROVIDER = "mm-auth"; private final ConfigurationEntryStore _store; private final Map _cliEntries; @@ -208,6 +209,10 @@ public class ManagementModeStoreHandler implements ConfigurationEntryStore attributes.put(Port.PORT, port); attributes.put(Port.PROTOCOLS, Collections.singleton(protocol)); attributes.put(Port.NAME, MANAGEMENT_MODE_PORT_PREFIX + protocol.name()); + if (protocol != Protocol.RMI) + { + attributes.put(Port.AUTHENTICATION_PROVIDER, MANAGEMENT_MODE_AUTH_PROVIDER); + } ConfigurationEntry portEntry = new ConfigurationEntry(UUID.randomUUID(), PORT_TYPE, attributes, Collections. emptySet(), this); if (LOGGER.isDebugEnabled()) diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties index 1aa7815c39..76c1fa1b5b 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/logging/messages/Broker_logmessages.properties @@ -45,4 +45,6 @@ STATS_MSGS = BRK-1009 : {0,choice,0#delivered|1#received} : {1,number,#.###} msg PLATFORM = BRK-1010 : Platform : JVM : {0} version: {1} OS : {2} version: {3} arch: {4} # 0 Maximum Memory -MAX_MEMORY = BRK-1011 : Maximum Memory : {0,number} bytes \ No newline at end of file +MAX_MEMORY = BRK-1011 : Maximum Memory : {0,number} bytes + +MANAGEMENT_MODE = BRK-1012 : Management Mode : User Details : {0} / {1} \ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java index 0e230bd552..712b53f627 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Broker.java @@ -53,7 +53,6 @@ public interface Broker extends ConfiguredObject String STATE = "state"; String TIME_TO_LIVE = "timeToLive"; String UPDATED = "updated"; - String DEFAULT_AUTHENTICATION_PROVIDER = "defaultAuthenticationProvider"; String DEFAULT_VIRTUAL_HOST = "defaultVirtualHost"; String STATISTICS_REPORTING_PERIOD = "statisticsReportingPeriod"; String STATISTICS_REPORTING_RESET_ENABLED = "statisticsReportingResetEnabled"; @@ -112,7 +111,6 @@ public interface Broker extends ConfiguredObject STATE, TIME_TO_LIVE, UPDATED, - DEFAULT_AUTHENTICATION_PROVIDER, DEFAULT_VIRTUAL_HOST, QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, @@ -151,8 +149,6 @@ public interface Broker extends ConfiguredObject LifetimePolicy lifetime, long ttl, Map attributes) throws AccessControlException, IllegalArgumentException; - AuthenticationProvider getDefaultAuthenticationProvider(); - Collection getGroupProviders(); /** @@ -172,6 +168,8 @@ public interface Broker extends ConfiguredObject */ LogRecorder getLogRecorder(); + AuthenticationProvider findAuthenticationProviderByName(String authenticationProviderName); + VirtualHost findVirtualHostByName(String name); KeyStore findKeyStoreByName(String name); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java index 7ff5f04ee7..60d62e3f27 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/Port.java @@ -93,7 +93,6 @@ public interface Port extends ConfiguredObject AccessControlException, IllegalArgumentException; - Collection getProtocols(); void addProtocol(Protocol protocol) throws IllegalStateException, @@ -104,12 +103,9 @@ public interface Port extends ConfiguredObject AccessControlException, IllegalArgumentException; + AuthenticationProvider getAuthenticationProvider(); //children Collection getVirtualHostBindings(); Collection getConnections(); - - AuthenticationProvider getAuthenticationProvider(); - - void setAuthenticationProvider(AuthenticationProvider authenticationProvider); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java index f788923b3a..8d601460ad 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/AuthenticationProviderAdapter.java @@ -219,10 +219,6 @@ public abstract class AuthenticationProviderAdapter ports = new ArrayList(_broker.getPorts()); for (Port port : ports) { diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java index ae30f70877..49af66126c 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/BrokerAdapter.java @@ -33,14 +33,15 @@ import java.util.UUID; import org.apache.log4j.Logger; import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.IllegalConfigurationException; -import org.apache.qpid.server.configuration.store.ManagementModeStoreHandler; import org.apache.qpid.server.configuration.updater.TaskExecutor; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; import org.apache.qpid.server.logging.actors.BrokerActor; import org.apache.qpid.server.logging.actors.CurrentActor; +import org.apache.qpid.server.logging.messages.BrokerMessages; import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.ConfigurationChangeListener; @@ -56,9 +57,12 @@ import org.apache.qpid.server.model.Statistics; import org.apache.qpid.server.model.TrustStore; import org.apache.qpid.server.model.UUIDGenerator; import org.apache.qpid.server.model.VirtualHost; +import org.apache.qpid.server.model.adapter.AuthenticationProviderAdapter.SimpleAuthenticationProviderAdapter; import org.apache.qpid.server.security.SecurityManager; import org.apache.qpid.server.security.SubjectCreator; import org.apache.qpid.server.security.access.Operation; +import org.apache.qpid.server.security.auth.manager.AuthenticationManager; +import org.apache.qpid.server.security.auth.manager.SimpleAuthenticationManager; import org.apache.qpid.server.security.group.FileGroupManager; import org.apache.qpid.server.security.group.GroupManager; import org.apache.qpid.server.stats.StatisticsGatherer; @@ -92,7 +96,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat put(ACL_FILE, String.class); put(NAME, String.class); put(DEFAULT_VIRTUAL_HOST, String.class); - put(DEFAULT_AUTHENTICATION_PROVIDER, String.class); put(GROUP_FILE, String.class); put(VIRTUALHOST_STORE_TRANSACTION_IDLE_TIMEOUT_CLOSE, Long.class); @@ -168,7 +171,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat private final Map _trustStores = new HashMap(); private final AuthenticationProviderFactory _authenticationProviderFactory; - private AuthenticationProvider _defaultAuthenticationProvider; private final PortFactory _portFactory; private final SecurityManager _securityManager; @@ -176,11 +178,12 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat private final Collection _supportedStoreTypes; private final ConfigurationEntryStore _brokerStore; - private boolean _managementMode; + private AuthenticationProvider _managementAuthenticationProvider; + private BrokerOptions _brokerOptions; public BrokerAdapter(UUID id, Map attributes, StatisticsGatherer statisticsGatherer, VirtualHostRegistry virtualHostRegistry, LogRecorder logRecorder, RootMessageLogger rootMessageLogger, AuthenticationProviderFactory authenticationProviderFactory, - PortFactory portFactory, TaskExecutor taskExecutor, ConfigurationEntryStore brokerStore) + PortFactory portFactory, TaskExecutor taskExecutor, ConfigurationEntryStore brokerStore, BrokerOptions brokerOptions) { super(id, DEFAULTS, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), taskExecutor); _statisticsGatherer = statisticsGatherer; @@ -195,7 +198,15 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat createBrokerChildrenFromAttributes(); _supportedStoreTypes = new MessageStoreCreator().getStoreTypes(); _brokerStore = brokerStore; - _managementMode = brokerStore instanceof ManagementModeStoreHandler; + _brokerOptions = brokerOptions; + createBrokerChildrenFromAttributes(); + if (_brokerOptions.isManagementMode()) + { + AuthenticationManager authManager = new SimpleAuthenticationManager(BrokerOptions.MANAGEMENT_MODE_USER_NAME, _brokerOptions.getManagementModePassword()); + AuthenticationProvider authenticationProvider = new SimpleAuthenticationProviderAdapter(UUID.randomUUID(), this, + authManager, Collections. emptyMap(), Collections. emptySet()); + _managementAuthenticationProvider = authenticationProvider; + } } /* @@ -204,6 +215,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat private void createBrokerChildrenFromAttributes() { createGroupProvider(); + } private void createGroupProvider() @@ -250,6 +262,10 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat public AuthenticationProvider findAuthenticationProviderByName(String authenticationProviderName) { + if (isManagementMode()) + { + return _managementAuthenticationProvider; + } Collection providers = getAuthenticationProviders(); for (AuthenticationProvider authenticationProvider : providers) { @@ -277,17 +293,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat } } - @Override - public AuthenticationProvider getDefaultAuthenticationProvider() - { - return _defaultAuthenticationProvider; - } - - public void setDefaultAuthenticationProvider(AuthenticationProvider provider) - { - _defaultAuthenticationProvider = provider; - } - @Override public Collection getGroupProviders() { @@ -766,6 +771,11 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat changeState(_portAdapters, currentState,State.ACTIVE, false); changeState(_plugins, currentState,State.ACTIVE, false); + + if (isManagementMode()) + { + CurrentActor.get().message(BrokerMessages.MANAGEMENT_MODE(BrokerOptions.MANAGEMENT_MODE_USER_NAME, _brokerOptions.getManagementModePassword())); + } return true; } else if (desiredState == State.STOPPED) @@ -956,7 +966,7 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat public SubjectCreator getSubjectCreator(SocketAddress localAddress) { InetSocketAddress inetSocketAddress = (InetSocketAddress)localAddress; - AuthenticationProvider provider = _defaultAuthenticationProvider; + AuthenticationProvider provider = null; Collection ports = getPorts(); for (Port p : ports) { @@ -966,6 +976,12 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat break; } } + + if(provider == null) + { + throw new IllegalConfigurationException("Unable to determine authentication provider for address: " + localAddress); + } + return provider.getSubjectCreator(); } @@ -1002,7 +1018,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat @Override protected void changeAttributes(Map attributes) { - //TODO: Add management mode check Map convertedAttributes = MapValueConverter.convert(attributes, ATTRIBUTE_TYPES); validateAttributes(convertedAttributes); @@ -1019,13 +1034,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat { createGroupProvider(); } - else if (DEFAULT_AUTHENTICATION_PROVIDER.equals(name)) - { - if (!_defaultAuthenticationProvider.getName().equals(desired)) - { - _defaultAuthenticationProvider = findAuthenticationProviderByName((String)desired); - } - } attributeSet(name, expected, desired); } @@ -1048,16 +1056,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat new FileGroupManager(groupFile); } - String defaultAuthenticationProvider = (String) convertedAttributes.get(DEFAULT_AUTHENTICATION_PROVIDER); - if (defaultAuthenticationProvider != null) - { - AuthenticationProvider provider = findAuthenticationProviderByName(defaultAuthenticationProvider); - if (provider == null) - { - throw new IllegalConfigurationException("Authentication provider with name " + defaultAuthenticationProvider - + " canot be set as a default as it does not exist"); - } - } String defaultVirtualHost = (String) convertedAttributes.get(DEFAULT_VIRTUAL_HOST); if (defaultVirtualHost != null) { @@ -1127,6 +1125,6 @@ public class BrokerAdapter extends AbstractAdapter implements Broker, Configurat @Override public boolean isManagementMode() { - return _managementMode; + return _brokerOptions.isManagementMode(); } } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java index 0c1249d20e..af6b1421a7 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/model/adapter/PortAdapter.java @@ -82,6 +82,24 @@ public class PortAdapter extends AbstractAdapter implements Port super(id, defaults, MapValueConverter.convert(attributes, ATTRIBUTE_TYPES), taskExecutor); _broker = broker; State state = MapValueConverter.getEnumAttribute(State.class, STATE, attributes, State.INITIALISING); + + Collection protocols = getProtocols(); + boolean rmiRegistry = protocols != null && protocols.contains(Protocol.RMI); + if (!rmiRegistry) + { + String authProvider = (String)getAttribute(Port.AUTHENTICATION_PROVIDER); + if (authProvider == null) + { + throw new IllegalConfigurationException("An authentication provider must be specified for port : " + getName()); + } + _authenticationProvider = broker.findAuthenticationProviderByName(authProvider); + + if(_authenticationProvider == null) + { + throw new IllegalConfigurationException("The authentication provider '" + authProvider + "' could not be found for port : " + getName()); + } + } + _state = new AtomicReference(state); addParent(Broker.class, broker); } @@ -350,11 +368,6 @@ public class PortAdapter extends AbstractAdapter implements Port return _authenticationProvider; } - public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) - { - _authenticationProvider = authenticationProvider; - } - @Override protected void changeAttributes(Map attributes) { @@ -451,6 +464,14 @@ public class PortAdapter extends AbstractAdapter implements Port + authenticationProviderName + "'"); } } + else + { + if (protocols != null && !protocols.contains(Protocol.RMI)) + { + throw new IllegalConfigurationException("An authentication provider must be specified"); + } + } + super.changeAttributes(converted); } diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java index 1379b375cf..fa5024c1fe 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/ApplicationRegistry.java @@ -27,6 +27,7 @@ import java.util.TimerTask; import org.apache.log4j.Logger; import org.apache.qpid.common.Closeable; import org.apache.qpid.common.QpidProperties; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; @@ -89,7 +90,7 @@ public class ApplicationRegistry implements IApplicationRegistry initialiseStatistics(); } - public void initialise() throws Exception + public void initialise(BrokerOptions brokerOptions) throws Exception { // Create the RootLogger to be used during broker operation boolean statusUpdatesEnabled = Boolean.parseBoolean(System.getProperty(BrokerProperties.PROPERTY_STATUS_UPDATES, "true")); @@ -112,7 +113,7 @@ public class ApplicationRegistry implements IApplicationRegistry _taskExecutor = new TaskExecutor(); _taskExecutor.start(); - RecovererProvider provider = new DefaultRecovererProvider((StatisticsGatherer)this, _virtualHostRegistry, _logRecorder, _rootMessageLogger, _taskExecutor); + RecovererProvider provider = new DefaultRecovererProvider((StatisticsGatherer)this, _virtualHostRegistry, _logRecorder, _rootMessageLogger, _taskExecutor, brokerOptions); ConfiguredObjectRecoverer brokerRecoverer = provider.getRecoverer(Broker.class.getSimpleName()); _broker = (Broker) brokerRecoverer.create(provider, _store.getRootEntry()); diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java index d12258d194..7341922bd0 100644 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/registry/IApplicationRegistry.java @@ -20,17 +20,14 @@ */ package org.apache.qpid.server.registry; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.stats.StatisticsGatherer; public interface IApplicationRegistry extends StatisticsGatherer { - /** - * Initialise the application registry. All initialisation must be done in this method so that any components - * that need access to the application registry itself for initialisation are able to use it. Attempting to - * initialise in the constructor will lead to failures since the registry reference will not have been set. - */ - void initialise() throws Exception; + + void initialise(BrokerOptions brokerOptions) throws Exception; /** * Shutdown this Registry diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticator.java new file mode 100644 index 0000000000..bf8d489e61 --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticator.java @@ -0,0 +1,136 @@ +/* + * + * 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.qpid.server.security.auth.jmx; + +import java.net.SocketAddress; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.security.SecurityManager; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; + +import javax.management.remote.JMXAuthenticator; +import javax.security.auth.Subject; + +public class JMXPasswordAuthenticator implements JMXAuthenticator +{ + static final String UNABLE_TO_LOOKUP = "The broker was unable to lookup the user details"; + static final String SHOULD_BE_STRING_ARRAY = "User details should be String[]"; + static final String SHOULD_HAVE_2_ELEMENTS = "User details should have 2 elements, username, password"; + static final String SHOULD_BE_NON_NULL = "Supplied username and password should be non-null"; + static final String INVALID_CREDENTIALS = "Invalid user details supplied"; + static final String USER_NOT_AUTHORISED_FOR_MANAGEMENT = "User not authorised for management"; + static final String CREDENTIALS_REQUIRED = "User details are required. " + + "Please ensure you are using an up to date management console to connect."; + + private final Broker _broker; + private final SocketAddress _address; + + public JMXPasswordAuthenticator(Broker broker, SocketAddress address) + { + _broker = broker; + _address = address; + } + + public Subject authenticate(Object credentials) throws SecurityException + { + validateCredentials(credentials); + + final String[] userCredentials = (String[]) credentials; + final String username = (String) userCredentials[0]; + final String password = (String) userCredentials[1]; + + final Subject authenticatedSubject = doAuthentication(username, password); + doManagementAuthorisation(authenticatedSubject); + return authenticatedSubject; + } + + private void validateCredentials(Object credentials) + { + // Verify that credential's are of type String[]. + if (!(credentials instanceof String[])) + { + if (credentials == null) + { + throw new SecurityException(CREDENTIALS_REQUIRED); + } + else + { + throw new SecurityException(SHOULD_BE_STRING_ARRAY); + } + } + + // Verify that required number of credentials. + if (((String[])credentials).length != 2) + { + throw new SecurityException(SHOULD_HAVE_2_ELEMENTS); + } + } + + private Subject doAuthentication(final String username, final String password) + { + // Verify that all required credentials are actually present. + if (username == null || password == null) + { + throw new SecurityException(SHOULD_BE_NON_NULL); + } + + SubjectCreator subjectCreator = _broker.getSubjectCreator(_address); + if (subjectCreator == null) + { + throw new SecurityException("Can't get subject creator for " + _address); + } + + final SubjectAuthenticationResult result = subjectCreator.authenticate(username, password); + + if (AuthenticationStatus.ERROR.equals(result.getStatus())) + { + throw new SecurityException("Authentication manager failed", result.getCause()); + } + else if (AuthenticationStatus.SUCCESS.equals(result.getStatus())) + { + return result.getSubject(); + } + else + { + throw new SecurityException(INVALID_CREDENTIALS); + } + } + + private void doManagementAuthorisation(Subject authenticatedSubject) + { + SecurityManager.setThreadSubject(authenticatedSubject); + try + { + if (!_broker.getSecurityManager().accessManagement()) + { + throw new SecurityException(USER_NOT_AUTHORISED_FOR_MANAGEMENT); + } + } + finally + { + SecurityManager.setThreadSubject(null); + } + } + + +} \ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java new file mode 100644 index 0000000000..903f54dd8e --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManager.java @@ -0,0 +1,214 @@ +/* + * 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.qpid.server.security.auth.manager; + +import java.io.IOException; +import java.security.Principal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.Sasl; +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.log4j.Logger; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.UsernamePrincipal; +import org.apache.qpid.server.security.auth.sasl.plain.PlainPasswordCallback; +import org.apache.qpid.server.security.auth.sasl.plain.PlainSaslServer; + +public class SimpleAuthenticationManager implements AuthenticationManager +{ + private static final Logger _logger = Logger.getLogger(SimpleAuthenticationManager.class); + + private static final String PLAIN_MECHANISM = "PLAIN"; + private static final String CRAM_MD5_MECHANISM = "CRAM-MD5"; + + private Map _users; + + public SimpleAuthenticationManager(String userName, String userPassword) + { + this(Collections.singletonMap(userName, userPassword)); + } + + public SimpleAuthenticationManager(Map users) + { + _users = new HashMap(users); + } + + @Override + public void initialise() + { + } + + @Override + public String getMechanisms() + { + return PLAIN_MECHANISM + " " + CRAM_MD5_MECHANISM; + } + + @Override + public SaslServer createSaslServer(String mechanism, String localFQDN, Principal externalPrincipal) throws SaslException + { + if (PLAIN_MECHANISM.equals(mechanism)) + { + return new PlainSaslServer(new SimplePlainCallbackHandler()); + } + else if (CRAM_MD5_MECHANISM.equals(mechanism)) + { + return Sasl.createSaslServer(mechanism, "AMQP", localFQDN, null, new SimpleCramMd5CallbackHandler()); + } + else + { + throw new SaslException("Unknown mechanism: " + mechanism); + } + } + + @Override + public AuthenticationResult authenticate(SaslServer server, byte[] response) + { + try + { + // Process response from the client + byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]); + + if (server.isComplete()) + { + String authorizationID = server.getAuthorizationID(); + _logger.debug("Authenticated as " + authorizationID); + + return new AuthenticationResult(new UsernamePrincipal(authorizationID)); + } + else + { + return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE); + } + } + catch (SaslException e) + { + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR, e); + } + } + + @Override + public AuthenticationResult authenticate(String username, String password) + { + if (_users.containsKey(username)) + { + String userPassword = _users.get(username); + if (userPassword.equals(password)) + { + return new AuthenticationResult(new UsernamePrincipal(username)); + } + } + return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR); + } + + @Override + public void close() + { + } + + @Override + public void onCreate() + { + // nothing to do, no external resource is required + } + + @Override + public void onDelete() + { + // nothing to do, no external resource is used + } + + private class SimpleCramMd5CallbackHandler implements CallbackHandler + { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + String username = null; + for (Callback callback : callbacks) + { + if (callback instanceof NameCallback) + { + username = ((NameCallback) callback).getDefaultName(); + } + else if (callback instanceof PasswordCallback) + { + if (_users.containsKey(username)) + { + String password = _users.get(username); + ((PasswordCallback) callback).setPassword(password.toCharArray()); + } + else + { + throw new SaslException("Authentication failed"); + } + } + else if (callback instanceof AuthorizeCallback) + { + ((AuthorizeCallback) callback).setAuthorized(true); + } + else + { + throw new UnsupportedCallbackException(callback); + } + } + } + } + + private class SimplePlainCallbackHandler implements CallbackHandler + { + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException + { + String username = null; + for (Callback callback : callbacks) + { + if (callback instanceof NameCallback) + { + username = ((NameCallback) callback).getDefaultName(); + } + else if (callback instanceof PlainPasswordCallback) + { + if (_users.containsKey(username)) + { + PlainPasswordCallback plainPasswordCallback = (PlainPasswordCallback) callback; + String password = plainPasswordCallback.getPlainPassword(); + plainPasswordCallback.setAuthenticated(password.equals(_users.get(username))); + } + } + else if (callback instanceof AuthorizeCallback) + { + ((AuthorizeCallback) callback).setAuthorized(true); + } + else + { + throw new UnsupportedCallbackException(callback); + } + } + } + } +} diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java deleted file mode 100644 index abb8677e90..0000000000 --- a/qpid/java/broker/src/main/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticator.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * - * 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.qpid.server.security.auth.rmi; - -import java.net.SocketAddress; - -import org.apache.qpid.server.model.Broker; -import org.apache.qpid.server.security.SecurityManager; -import org.apache.qpid.server.security.SubjectCreator; -import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; -import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; - -import javax.management.remote.JMXAuthenticator; -import javax.security.auth.Subject; - -public class RMIPasswordAuthenticator implements JMXAuthenticator -{ - static final String UNABLE_TO_LOOKUP = "The broker was unable to lookup the user details"; - static final String SHOULD_BE_STRING_ARRAY = "User details should be String[]"; - static final String SHOULD_HAVE_2_ELEMENTS = "User details should have 2 elements, username, password"; - static final String SHOULD_BE_NON_NULL = "Supplied username and password should be non-null"; - static final String INVALID_CREDENTIALS = "Invalid user details supplied"; - static final String USER_NOT_AUTHORISED_FOR_MANAGEMENT = "User not authorised for management"; - static final String CREDENTIALS_REQUIRED = "User details are required. " + - "Please ensure you are using an up to date management console to connect."; - - private final Broker _broker; - private final SocketAddress _address; - - public RMIPasswordAuthenticator(Broker broker, SocketAddress address) - { - _broker = broker; - _address = address; - } - - public Subject authenticate(Object credentials) throws SecurityException - { - validateCredentials(credentials); - - final String[] userCredentials = (String[]) credentials; - final String username = (String) userCredentials[0]; - final String password = (String) userCredentials[1]; - - final Subject authenticatedSubject = doAuthentication(username, password); - doManagementAuthorisation(authenticatedSubject); - return authenticatedSubject; - } - - private void validateCredentials(Object credentials) - { - // Verify that credential's are of type String[]. - if (!(credentials instanceof String[])) - { - if (credentials == null) - { - throw new SecurityException(CREDENTIALS_REQUIRED); - } - else - { - throw new SecurityException(SHOULD_BE_STRING_ARRAY); - } - } - - // Verify that required number of credentials. - if (((String[])credentials).length != 2) - { - throw new SecurityException(SHOULD_HAVE_2_ELEMENTS); - } - } - - private Subject doAuthentication(final String username, final String password) - { - // Verify that all required credentials are actually present. - if (username == null || password == null) - { - throw new SecurityException(SHOULD_BE_NON_NULL); - } - - SubjectCreator subjectCreator = _broker.getSubjectCreator(_address); - if (subjectCreator == null) - { - throw new SecurityException("Can't get subject creator for " + _address); - } - - final SubjectAuthenticationResult result = subjectCreator.authenticate(username, password); - - if (AuthenticationStatus.ERROR.equals(result.getStatus())) - { - throw new SecurityException("Authentication manager failed", result.getCause()); - } - else if (AuthenticationStatus.SUCCESS.equals(result.getStatus())) - { - return result.getSubject(); - } - else - { - throw new SecurityException(INVALID_CREDENTIALS); - } - } - - private void doManagementAuthorisation(Subject authenticatedSubject) - { - SecurityManager.setThreadSubject(authenticatedSubject); - try - { - if (!_broker.getSecurityManager().accessManagement()) - { - throw new SecurityException(USER_NOT_AUTHORISED_FOR_MANAGEMENT); - } - } - finally - { - SecurityManager.setThreadSubject(null); - } - } - - -} \ No newline at end of file diff --git a/qpid/java/broker/src/main/java/org/apache/qpid/server/util/StringUtil.java b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/StringUtil.java new file mode 100644 index 0000000000..aa17a9493b --- /dev/null +++ b/qpid/java/broker/src/main/java/org/apache/qpid/server/util/StringUtil.java @@ -0,0 +1,44 @@ +/* + * + * 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.qpid.server.util; + +import java.util.Random; + +public class StringUtil +{ + private static final String NUMBERS = "0123456789"; + private static final String LETTERS = "abcdefghijklmnopqrstuvwxwy"; + private static final String OTHERS = "_-"; + private static final char[] CHARACTERS = (NUMBERS + LETTERS + LETTERS.toUpperCase() + OTHERS).toCharArray(); + + private Random _random = new Random(); + + public String randomAlphaNumericString(int maxLength) + { + char[] result = new char[maxLength]; + for (int i = 0; i < maxLength; i++) + { + result[i] = (char) CHARACTERS[_random.nextInt(CHARACTERS.length)]; + } + return new String(result); + } + +} diff --git a/qpid/java/broker/src/main/resources/initial-config.json b/qpid/java/broker/src/main/resources/initial-config.json index 44f8fed789..fde3c3a96e 100644 --- a/qpid/java/broker/src/main/resources/initial-config.json +++ b/qpid/java/broker/src/main/resources/initial-config.json @@ -21,7 +21,6 @@ { "name": "QpidBroker", "storeVersion": 1, - "defaultAuthenticationProvider" : "passwordFile", "defaultVirtualHost" : "default", "authenticationproviders" : [ { "name" : "passwordFile", @@ -30,10 +29,12 @@ } ], "ports" : [ { "name" : "AMQP", - "port" : 5672 + "port" : 5672, + "authenticationProvider" : "passwordFile" }, { "name" : "HTTP", "port" : 8080, + "authenticationProvider" : "passwordFile", "protocols" : [ "HTTP" ] }, { "name" : "RMI_REGISTRY", @@ -42,6 +43,7 @@ }, { "name" : "JMX_CONNECTOR", "port" : 9099, + "authenticationProvider" : "passwordFile", "protocols" : [ "JMX_RMI" ] }], "virtualhosts" : [ { diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java index c1acf81a1a..08031c36a4 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/BrokerOptionsTest.java @@ -220,4 +220,10 @@ public class BrokerOptionsTest extends QpidTestCase _options.setOverwriteConfigurationStore(true); assertTrue(_options.isOverwriteConfigurationStore()); } + + public void testManagementModePassword() + { + _options.setManagementModePassword("test"); + assertEquals("Unexpected management mode password", "test", _options.getManagementModePassword()); + } } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java index aab919a828..d7579e2b2a 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/MainTest.java @@ -171,6 +171,28 @@ public class MainTest extends QpidTestCase assertEquals(0, options.getManagementModeHttpPort()); } + public void testManagementModePassword() + { + String password = getTestName(); + BrokerOptions options = startDummyMain("-mm -mmpass " + password); + assertTrue(options.isManagementMode()); + assertEquals(password, options.getManagementModePassword()); + + options = startDummyMain("-mm --management-mode-password " + password); + assertTrue(options.isManagementMode()); + assertEquals(password, options.getManagementModePassword()); + + options = startDummyMain("-mmpass " + password); + assertNotNull(options.getManagementModePassword()); + } + + public void testDefaultManagementModePassword() + { + BrokerOptions options = startDummyMain("-mm"); + assertTrue(options.isManagementMode()); + assertNotNull(options.getManagementModePassword()); + } + private BrokerOptions startDummyMain(String commandLine) { return (new TestMain(commandLine.split("\\s"))).getOptions(); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/BrokerRecovererTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/BrokerRecovererTest.java index 3a41b61961..5a8580fd26 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/BrokerRecovererTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/BrokerRecovererTest.java @@ -20,10 +20,8 @@ */ package org.apache.qpid.server.configuration.startup; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.mockito.Mockito.verify; import java.util.Arrays; import java.util.Collection; @@ -35,9 +33,9 @@ import java.util.UUID; import junit.framework.TestCase; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; -import org.apache.qpid.server.configuration.IllegalConfigurationException; import org.apache.qpid.server.configuration.RecovererProvider; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; @@ -73,7 +71,7 @@ public class BrokerRecovererTest extends TestCase super.setUp(); _brokerRecoverer = new BrokerRecoverer(mock(AuthenticationProviderFactory.class), mock(PortFactory.class), mock(StatisticsGatherer.class), - mock(VirtualHostRegistry.class), mock(LogRecorder.class), mock(RootMessageLogger.class), mock(TaskExecutor.class)); + mock(VirtualHostRegistry.class), mock(LogRecorder.class), mock(RootMessageLogger.class), mock(TaskExecutor.class), mock(BrokerOptions.class)); when(_brokerEntry.getId()).thenReturn(_brokerId); when(_brokerEntry.getChildren()).thenReturn(_brokerEntryChildren); @@ -89,7 +87,6 @@ public class BrokerRecovererTest extends TestCase { Map attributes = new HashMap(); attributes.put(Broker.DEFAULT_VIRTUAL_HOST, "test"); - attributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider1"); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, 9l); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 8l); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 7l); @@ -172,24 +169,6 @@ public class BrokerRecovererTest extends TestCase assertEquals(Collections.singletonList(port), broker.getPorts()); } - public void testCreateBrokerWithoutAuthenticationProviderThrowsException() - { - assertNotNull("expected to remove the base entry", _brokerEntryChildren.remove(AuthenticationProvider.class.getSimpleName())); - assertTrue("should be empty", _brokerEntryChildren.isEmpty()); - - RecovererProvider recovererProvider = createRecoveryProvider(new ConfigurationEntry[0], new ConfiguredObject[0]); - - try - { - _brokerRecoverer.create(recovererProvider, _brokerEntry); - fail("should have thrown an exception due to missing authentication provider configuration"); - } - catch(IllegalConfigurationException e) - { - //expected - } - } - public void testCreateBrokerWithOneAuthenticationProvider() { RecovererProvider recovererProvider = createRecoveryProvider(new ConfigurationEntry[]{_authenticationProviderEntry1}, @@ -202,29 +181,6 @@ public class BrokerRecovererTest extends TestCase assertEquals(Collections.singletonList(_authenticationProvider1), broker.getAuthenticationProviders()); } - public void testCreateBrokerWithMultipleAuthenticationProvidersAndNoDefaultThrowsException() - { - AuthenticationProvider authenticationProvider2 = mock(AuthenticationProvider.class); - when(authenticationProvider2.getName()).thenReturn("authenticationProvider2"); - ConfigurationEntry authenticationProviderEntry2 = mock(ConfigurationEntry.class); - _brokerEntryChildren.put(AuthenticationProvider.class.getSimpleName(), Arrays.asList(_authenticationProviderEntry1, authenticationProviderEntry2)); - - Map emptyBrokerAttributes = new HashMap(); - when(_brokerEntry.getAttributes()).thenReturn(emptyBrokerAttributes); - - RecovererProvider recovererProvider = createRecoveryProvider(new ConfigurationEntry[]{authenticationProviderEntry2, _authenticationProviderEntry1}, - new ConfiguredObject[]{authenticationProvider2, _authenticationProvider1}); - try - { - _brokerRecoverer.create(recovererProvider, _brokerEntry); - fail("should have thrown an exception due to missing authentication provider default"); - } - catch(IllegalConfigurationException e) - { - //expected - } - } - public void testCreateBrokerWithMultipleAuthenticationProvidersAndPorts() { //Create a second authentication provider @@ -233,13 +189,10 @@ public class BrokerRecovererTest extends TestCase ConfigurationEntry authenticationProviderEntry2 = mock(ConfigurationEntry.class); _brokerEntryChildren.put(AuthenticationProvider.class.getSimpleName(), Arrays.asList(_authenticationProviderEntry1, authenticationProviderEntry2)); - //Set the default authentication provider Map brokerAtttributes = new HashMap(); when(_brokerEntry.getAttributes()).thenReturn(brokerAtttributes); - brokerAtttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider2"); - //Add a couple ports, one with a defined authentication provider and - //one without (which should then use the default) + //Add a couple ports ConfigurationEntry portEntry1 = mock(ConfigurationEntry.class); Port port1 = mock(Port.class); when(port1.getName()).thenReturn("port1"); @@ -249,6 +202,7 @@ public class BrokerRecovererTest extends TestCase Port port2 = mock(Port.class); when(port2.getName()).thenReturn("port2"); when(port2.getPort()).thenReturn(5672); + when(port2.getAttribute(Port.AUTHENTICATION_PROVIDER)).thenReturn("authenticationProvider2"); _brokerEntryChildren.put(Port.class.getSimpleName(), Arrays.asList(portEntry1, portEntry2)); RecovererProvider recovererProvider = createRecoveryProvider( @@ -258,47 +212,12 @@ public class BrokerRecovererTest extends TestCase Broker broker = _brokerRecoverer.create(recovererProvider, _brokerEntry); assertNotNull(broker); - assertEquals("Unexpected number of authentication providers", 2,broker.getAuthenticationProviders().size()); + assertEquals("Unexpected number of authentication providers", 2, broker.getAuthenticationProviders().size()); Collection ports = broker.getPorts(); assertEquals("Unexpected number of ports", 2, ports.size()); assertTrue(ports.contains(port1)); assertTrue(ports.contains(port2)); - - verify(port1).setAuthenticationProvider(any(AuthenticationProvider.class)); - verify(port1).setAuthenticationProvider(_authenticationProvider1); - - verify(port2).setAuthenticationProvider(any(AuthenticationProvider.class)); - verify(port2).setAuthenticationProvider(authenticationProvider2); - } - - public void testCreateBrokerAssignsGroupAccessorToAuthenticationProviders() - { - //Create a second authentication provider - AuthenticationProvider authenticationProvider2 = mock(AuthenticationProvider.class); - when(authenticationProvider2.getName()).thenReturn("authenticationProvider2"); - ConfigurationEntry authenticationProviderEntry2 = mock(ConfigurationEntry.class); - _brokerEntryChildren.put(AuthenticationProvider.class.getSimpleName(), Arrays.asList(_authenticationProviderEntry1, authenticationProviderEntry2)); - - //Set the default authentication provider - Map brokerAtttributes = new HashMap(); - when(_brokerEntry.getAttributes()).thenReturn(brokerAtttributes); - brokerAtttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider2"); - - //Create a group provider - ConfigurationEntry groupProviderEntry = mock(ConfigurationEntry.class); - GroupProvider groupProvider = mock(GroupProvider.class); - _brokerEntryChildren.put(GroupProvider.class.getSimpleName(), Arrays.asList(groupProviderEntry)); - - RecovererProvider recovererProvider = createRecoveryProvider( - new ConfigurationEntry[]{groupProviderEntry, authenticationProviderEntry2, _authenticationProviderEntry1}, - new ConfiguredObject[]{groupProvider, authenticationProvider2, _authenticationProvider1}); - - Broker broker = _brokerRecoverer.create(recovererProvider, _brokerEntry); - - assertNotNull(broker); - assertEquals("Unexpected number of authentication providers", 2, broker.getAuthenticationProviders().size()); - } public void testCreateBrokerWithGroupProvider() diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProviderTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProviderTest.java index 96f2474c2d..b958ba1f56 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProviderTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/startup/DefaultRecovererProviderTest.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.configuration.startup; import static org.mockito.Mockito.mock; import junit.framework.TestCase; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; import org.apache.qpid.server.logging.LogRecorder; import org.apache.qpid.server.logging.RootMessageLogger; @@ -54,7 +55,7 @@ public class DefaultRecovererProviderTest extends TestCase RootMessageLogger rootMessageLogger = mock(RootMessageLogger.class); TaskExecutor taskExecutor = mock(TaskExecutor.class); - DefaultRecovererProvider provider = new DefaultRecovererProvider(statisticsGatherer, virtualHostRegistry, logRecorder, rootMessageLogger, taskExecutor); + DefaultRecovererProvider provider = new DefaultRecovererProvider(statisticsGatherer, virtualHostRegistry, logRecorder, rootMessageLogger, taskExecutor, mock(BrokerOptions.class)); for (String configuredObjectType : supportedTypes) { ConfiguredObjectRecoverer recovever = provider.getRecoverer(configuredObjectType); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java index 7d253d56f0..fc21706bc0 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/configuration/store/ConfigurationEntryStoreTestCase.java @@ -60,7 +60,6 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase _brokerId = UUID.randomUUID(); _brokerAttributes = new HashMap(); _brokerAttributes.put(Broker.DEFAULT_VIRTUAL_HOST, "test"); - _brokerAttributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider1"); _brokerAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, 9); _brokerAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 8); _brokerAttributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 7); @@ -171,7 +170,6 @@ public abstract class ConfigurationEntryStoreTestCase extends QpidTestCase ConfigurationEntry brokerConfigEntry = _store.getRootEntry(); Map attributes = new HashMap(); attributes.put(Broker.DEFAULT_VIRTUAL_HOST, "test"); - attributes.put(Broker.DEFAULT_AUTHENTICATION_PROVIDER, "authenticationProvider1"); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_MESSAGE_AGE, 19); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_MESSAGES, 18); attributes.put(Broker.QUEUE_ALERT_THRESHOLD_QUEUE_DEPTH_BYTES, 17); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/BrokerShutdownTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/BrokerShutdownTest.java index 7c1db6348b..eb5c672eb8 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/BrokerShutdownTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/BrokerShutdownTest.java @@ -23,6 +23,7 @@ package org.apache.qpid.server.model; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import org.apache.qpid.server.BrokerOptions; import org.apache.qpid.server.configuration.ConfigurationEntry; import org.apache.qpid.server.configuration.ConfigurationEntryStore; import org.apache.qpid.server.configuration.ConfiguredObjectRecoverer; @@ -121,7 +122,7 @@ public class BrokerShutdownTest extends QpidTestCase RootMessageLogger rootMessageLogger = mock(RootMessageLogger.class); // recover the broker from the store - RecovererProvider provider = new DefaultRecovererProvider(statisticsGatherer, virtualHostRegistry, logRecorder, rootMessageLogger, _taskExecutor); + RecovererProvider provider = new DefaultRecovererProvider(statisticsGatherer, virtualHostRegistry, logRecorder, rootMessageLogger, _taskExecutor, mock(BrokerOptions.class)); ConfiguredObjectRecoverer brokerRecoverer = provider.getRecoverer(Broker.class.getSimpleName()); Broker broker = (Broker) brokerRecoverer.create(provider, store.getRootEntry()); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java index 53fb1a0620..54826b8c88 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/model/adapter/PortFactoryTest.java @@ -36,6 +36,7 @@ import java.util.UUID; import org.apache.qpid.server.configuration.BrokerProperties; import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.AuthenticationProvider; import org.apache.qpid.server.model.Broker; import org.apache.qpid.server.model.KeyStore; import org.apache.qpid.server.model.Port; @@ -58,19 +59,22 @@ public class PortFactoryTest extends QpidTestCase private Broker _broker = mock(Broker.class); private KeyStore _keyStore = mock(KeyStore.class); private TrustStore _trustStore = mock(TrustStore.class); - + private String _authProviderName = "authProvider"; + private AuthenticationProvider _authProvider = mock(AuthenticationProvider.class); private PortFactory _portFactory; @Override protected void setUp() throws Exception { + when(_broker.findAuthenticationProviderByName(_authProviderName)).thenReturn(_authProvider); + setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_EXCLUDES, null); setTestSystemProperty(BrokerProperties.PROPERTY_BROKER_DEFAULT_AMQP_PROTOCOL_INCLUDES, null); _portFactory = new PortFactory(); _attributes.put(Port.PORT, _portNumber); _attributes.put(Port.TRANSPORTS, _tcpStringSet); - + _attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName); _attributes.put(Port.TCP_NO_DELAY, "true"); _attributes.put(Port.RECEIVE_BUFFER_SIZE, "1"); _attributes.put(Port.SEND_BUFFER_SIZE, "2"); @@ -111,6 +115,7 @@ public class PortFactoryTest extends QpidTestCase { Map attributes = new HashMap(); attributes.put(Port.PORT, 1); + attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName); Port port = _portFactory.createPort(_portId, _broker, attributes); assertNotNull(port); @@ -273,6 +278,7 @@ public class PortFactoryTest extends QpidTestCase Set nonAmqpStringSet = Collections.singleton(Protocol.JMX_RMI.name()); _attributes = new HashMap(); _attributes.put(Port.PROTOCOLS, nonAmqpStringSet); + _attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName); _attributes.put(Port.PORT, _portNumber); _attributes.put(Port.TRANSPORTS, _tcpStringSet); @@ -298,6 +304,7 @@ public class PortFactoryTest extends QpidTestCase Set nonAmqpStringSet = Collections.singleton(Protocol.JMX_RMI.name()); _attributes = new HashMap(); _attributes.put(Port.PROTOCOLS, nonAmqpStringSet); + _attributes.put(Port.AUTHENTICATION_PROVIDER, _authProviderName); _attributes.put(Port.PORT, _portNumber); Port port = _portFactory.createPort(_portId, _broker, _attributes); diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java deleted file mode 100644 index c41b9bf081..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/database/PropertiesPrincipalDatabase.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.qpid.server.security.auth.database; - -import org.apache.qpid.server.security.auth.sasl.AuthenticationProviderInitialiser; -import org.apache.qpid.server.security.auth.UsernamePrincipal; -import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5Initialiser; -import org.apache.qpid.server.security.auth.sasl.plain.PlainInitialiser; - -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.login.AccountNotFoundException; - -import java.io.File; -import java.io.IOException; -import java.security.Principal; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -public class PropertiesPrincipalDatabase implements PrincipalDatabase -{ - private Properties _users; - - private Map _saslServers; - - public PropertiesPrincipalDatabase(Properties users) - { - _users = users; - - _saslServers = new HashMap(); - - /** - * Create Authenticators for Properties Principal Database. - */ - - // Accept MD5 incomming and use plain comparison with the file - PlainInitialiser cram = new PlainInitialiser(); - cram.initialise(this); - // Accept Plain incomming and hash it for comparison to the file. - CRAMMD5Initialiser plain = new CRAMMD5Initialiser(); - plain.initialise(this, CRAMMD5Initialiser.HashDirection.INCOMMING); - - _saslServers.put(plain.getMechanismName(), cram); - _saslServers.put(cram.getMechanismName(), plain); - } - - public void setPassword(Principal principal, PasswordCallback callback) throws IOException, AccountNotFoundException - { - if (principal == null) - { - throw new IllegalArgumentException("principal must not be null"); - } - - - - final String pwd = _users.getProperty(principal.getName()); - - if (pwd != null) - { - callback.setPassword(pwd.toCharArray()); - } - else - { - throw new AccountNotFoundException("No account found for principal " + principal); - } - } - - public boolean verifyPassword(String principal, char[] password) throws AccountNotFoundException - { - //fixme this is not correct as toCharArray is not safe based on the type of string. - char[] pwd = _users.getProperty(principal).toCharArray(); - - return compareCharArray(pwd, password); - } - - public boolean updatePassword(Principal principal, char[] password) throws AccountNotFoundException - { - return false; // updates denied - } - - public boolean createPrincipal(Principal principal, char[] password) - { - return false; // updates denied - } - - public boolean deletePrincipal(Principal principal) throws AccountNotFoundException - { - return false; // updates denied - } - - private boolean compareCharArray(char[] a, char[] b) - { - boolean equal = false; - if (a.length == b.length) - { - equal = true; - int index = 0; - while (equal && index < a.length) - { - equal = a[index] == b[index]; - index++; - } - } - return equal; - } - - - public Map getMechanisms() - { - return _saslServers; - } - - public List getUsers() - { - return new LinkedList(); //todo - } - - public Principal getUser(String username) - { - if (_users.getProperty(username) != null) - { - return new UsernamePrincipal(username); - } - else - { - return null; - } - } - - public void reload() throws IOException - { - //No file to update from, so do nothing. - } - - @Override - public void open(File passwordFile) - { - throw new UnsupportedOperationException(); - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticatorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticatorTest.java new file mode 100644 index 0000000000..a4dd97e6a1 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/jmx/JMXPasswordAuthenticatorTest.java @@ -0,0 +1,259 @@ +/* + * + * 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.qpid.server.security.auth.jmx; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.Principal; +import java.util.regex.Pattern; + +import javax.security.auth.Subject; + +import junit.framework.TestCase; + +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.security.SubjectCreator; +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.jmx.JMXPasswordAuthenticator; +import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; +import org.apache.qpid.server.security.SecurityManager; + +/** + * Tests the JMXPasswordAuthenticator and its collaboration with the AuthenticationManager. + * + */ +public class JMXPasswordAuthenticatorTest extends TestCase +{ + private static final String USERNAME = "guest"; + private static final String PASSWORD = "password"; + + private final Broker _broker = mock(Broker.class); + private final SecurityManager _securityManager = mock(SecurityManager.class); + private final Subject _loginSubject = new Subject(); + private final String[] _credentials = new String[] {USERNAME, PASSWORD}; + + private JMXPasswordAuthenticator _rmipa; + + private SubjectCreator _usernamePasswordOkaySuvjectCreator = createMockSubjectCreator(true, null); + private SubjectCreator _badPasswordSubjectCreator = createMockSubjectCreator(false, null); + + protected void setUp() throws Exception + { + when(_broker.getSecurityManager()).thenReturn(_securityManager); + _rmipa = new JMXPasswordAuthenticator(_broker, new InetSocketAddress(8999)); + } + + /** + * Tests a successful authentication. Ensures that the expected subject is returned. + */ + public void testAuthenticationSuccess() + { + when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(_usernamePasswordOkaySuvjectCreator); + when(_securityManager.accessManagement()).thenReturn(true); + + Subject newSubject = _rmipa.authenticate(_credentials); + assertSame("Subject must be unchanged", _loginSubject, newSubject); + } + + /** + * Tests a unsuccessful authentication. + */ + public void testUsernameOrPasswordInvalid() + { + when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(_badPasswordSubjectCreator); + + try + { + _rmipa.authenticate(_credentials); + fail("Exception not thrown"); + } + catch (SecurityException se) + { + assertEquals("Unexpected exception message", + JMXPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); + } + } + + public void testAuthorisationFailure() + { + when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(_usernamePasswordOkaySuvjectCreator); + when(_securityManager.accessManagement()).thenReturn(false); + + try + { + _rmipa.authenticate(_credentials); + fail("Exception not thrown"); + } + catch (SecurityException se) + { + assertEquals("Unexpected exception message", + JMXPasswordAuthenticator.USER_NOT_AUTHORISED_FOR_MANAGEMENT, se.getMessage()); + } + } + + public void testSubjectCreatorInternalFailure() + { + final Exception mockAuthException = new Exception("Mock Auth system failure"); + SubjectCreator subjectCreator = createMockSubjectCreator(false, mockAuthException); + when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(subjectCreator); + + try + { + _rmipa.authenticate(_credentials); + fail("Exception not thrown"); + } + catch (SecurityException se) + { + assertEquals("Initial cause not found", mockAuthException, se.getCause()); + } + } + + /** + * Tests case where authentication manager is not set. + */ + public void testNullSubjectCreator() throws Exception + { + when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(null); + + try + { + _rmipa.authenticate(_credentials); + fail("SecurityException expected due to lack of authentication manager"); + } + catch (SecurityException se) + { + assertTrue("Unexpected exception message", Pattern.matches("Can't get subject creator for .*:8999", se.getMessage())); + } + } + + /** + * Tests case where arguments are non-Strings.. + */ + public void testWithNonStringArrayArgument() + { + // Test handling of non-string credential's + final Object[] objCredentials = new Object[]{USERNAME, PASSWORD}; + try + { + _rmipa.authenticate(objCredentials); + fail("SecurityException expected due to non string[] credentials"); + } + catch (SecurityException se) + { + assertEquals("Unexpected exception message", + JMXPasswordAuthenticator.SHOULD_BE_STRING_ARRAY, se.getMessage()); + } + } + + /** + * Tests case where there are too many, too few or null arguments. + */ + public void testWithIllegalNumberOfArguments() + { + String[] credentials; + + // Test handling of incorrect number of credentials + try + { + credentials = new String[]{USERNAME, PASSWORD, PASSWORD}; + _rmipa.authenticate(credentials); + fail("SecurityException expected due to supplying wrong number of credentials"); + } + catch (SecurityException se) + { + assertEquals("Unexpected exception message", + JMXPasswordAuthenticator.SHOULD_HAVE_2_ELEMENTS, se.getMessage()); + } + + // Test handling of null credentials + try + { + //send a null array + credentials = null; + _rmipa.authenticate(credentials); + fail("SecurityException expected due to not supplying an array of credentials"); + } + catch (SecurityException se) + { + assertEquals("Unexpected exception message", + JMXPasswordAuthenticator.CREDENTIALS_REQUIRED, se.getMessage()); + } + + try + { + //send a null password + credentials = new String[]{USERNAME, null}; + _rmipa.authenticate(credentials); + fail("SecurityException expected due to sending a null password"); + } + catch (SecurityException se) + { + assertEquals("Unexpected exception message", + JMXPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage()); + } + + try + { + //send a null username + credentials = new String[]{null, PASSWORD}; + _rmipa.authenticate(credentials); + fail("SecurityException expected due to sending a null username"); + } + catch (SecurityException se) + { + assertEquals("Unexpected exception message", + JMXPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage()); + } + } + + private SubjectCreator createMockSubjectCreator(final boolean successfulAuth, final Exception exception) + { + SubjectCreator subjectCreator = mock(SubjectCreator.class); + + SubjectAuthenticationResult subjectAuthenticationResult; + + if (exception != null) { + + subjectAuthenticationResult = new SubjectAuthenticationResult( + new AuthenticationResult(AuthenticationStatus.ERROR, exception)); + } + else if (successfulAuth) + { + + subjectAuthenticationResult = new SubjectAuthenticationResult( + new AuthenticationResult(mock(Principal.class)), _loginSubject); + } + else + { + subjectAuthenticationResult = new SubjectAuthenticationResult(new AuthenticationResult(AuthenticationStatus.CONTINUE)); + } + + when(subjectCreator.authenticate(anyString(), anyString())).thenReturn(subjectAuthenticationResult); + + return subjectCreator; + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManagerTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManagerTest.java new file mode 100644 index 0000000000..5868f44b8f --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/manager/SimpleAuthenticationManagerTest.java @@ -0,0 +1,140 @@ +package org.apache.qpid.server.security.auth.manager; + +import java.security.Principal; +import java.util.Set; + +import javax.security.sasl.SaslException; +import javax.security.sasl.SaslServer; + +import org.apache.qpid.server.security.auth.AuthenticationResult; +import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; +import org.apache.qpid.server.security.auth.sasl.SaslUtil; +import org.apache.qpid.server.security.auth.sasl.plain.PlainSaslServer; +import org.apache.qpid.test.utils.QpidTestCase; + +public class SimpleAuthenticationManagerTest extends QpidTestCase +{ + private static final String TEST_USER = "testUser"; + private static final String TEST_PASSWORD = "testPassword"; + private AuthenticationManager _authenticationManager; + + public void setUp() throws Exception + { + super.setUp(); + _authenticationManager = new SimpleAuthenticationManager(TEST_USER, TEST_PASSWORD); + } + + public void testGetMechanisms() + { + assertEquals("Unexpected mechanisms", "PLAIN CRAM-MD5", _authenticationManager.getMechanisms()); + } + + public void testCreateSaslServerForUnsupportedMechanisms() throws Exception + { + String[] unsupported = new String[] { "EXTERNAL", "CRAM-MD5-HEX", "CRAM-MD5-HASHED", "ANONYMOUS", "GSSAPI"}; + for (int i = 0; i < unsupported.length; i++) + { + String mechanism = unsupported[i]; + try + { + _authenticationManager.createSaslServer(mechanism, "test", null); + fail("Mechanism " + mechanism + " should not be supported by SimpleAuthenticationManager"); + } + catch (SaslException e) + { + // pass + } + } + } + + public void testAuthenticateWithPlainSaslServer() throws Exception + { + AuthenticationResult result = authenticatePlain(TEST_USER, TEST_PASSWORD); + assertAuthenticated(result); + } + + public void testAuthenticateWithPlainSaslServerInvalidPassword() throws Exception + { + AuthenticationResult result = authenticatePlain(TEST_USER, "wrong-password"); + assertUnauthenticated(result); + } + + public void testAuthenticateWithPlainSaslServerInvalidUsername() throws Exception + { + AuthenticationResult result = authenticatePlain("wrong-user", TEST_PASSWORD); + assertUnauthenticated(result); + } + + public void testAuthenticateWithCramMd5SaslServer() throws Exception + { + AuthenticationResult result = authenticateCramMd5(TEST_USER, TEST_PASSWORD); + assertAuthenticated(result); + } + + public void testAuthenticateWithCramMd5SaslServerInvalidPassword() throws Exception + { + AuthenticationResult result = authenticateCramMd5(TEST_USER, "wrong-password"); + assertUnauthenticated(result); + } + + public void testAuthenticateWithCramMd5SaslServerInvalidUsername() throws Exception + { + AuthenticationResult result = authenticateCramMd5("wrong-user", TEST_PASSWORD); + assertUnauthenticated(result); + } + + public void testAuthenticateValidCredentials() + { + AuthenticationResult result = _authenticationManager.authenticate(TEST_USER, TEST_PASSWORD); + assertEquals("Unexpected authentication result", AuthenticationStatus.SUCCESS, result.getStatus()); + assertAuthenticated(result); + } + + public void testAuthenticateInvalidPassword() + { + AuthenticationResult result = _authenticationManager.authenticate(TEST_USER, "invalid"); + assertUnauthenticated(result); + } + + public void testAuthenticateInvalidUserName() + { + AuthenticationResult result = _authenticationManager.authenticate("invalid", TEST_PASSWORD); + assertUnauthenticated(result); + } + + private void assertAuthenticated(AuthenticationResult result) + { + assertEquals("Unexpected authentication result", AuthenticationStatus.SUCCESS, result.getStatus()); + Principal principal = result.getMainPrincipal(); + assertEquals("Unexpected principal name", TEST_USER, principal.getName()); + Set principals = result.getPrincipals(); + assertEquals("Unexpected principals size", 1, principals.size()); + assertEquals("Unexpected principal name", TEST_USER, principals.iterator().next().getName()); + } + + private void assertUnauthenticated(AuthenticationResult result) + { + assertEquals("Unexpected authentication result", AuthenticationStatus.ERROR, result.getStatus()); + assertNull("Unexpected principal", result.getMainPrincipal()); + Set principals = result.getPrincipals(); + assertEquals("Unexpected principals size", 0, principals.size()); + } + + private AuthenticationResult authenticatePlain(String userName, String userPassword) throws SaslException, Exception + { + PlainSaslServer ss = (PlainSaslServer) _authenticationManager.createSaslServer("PLAIN", "test", null); + byte[] response = SaslUtil.generatePlainClientResponse(userName, userPassword); + + return _authenticationManager.authenticate(ss, response); + } + + private AuthenticationResult authenticateCramMd5(String userName, String userPassword) throws SaslException, Exception + { + SaslServer ss = _authenticationManager.createSaslServer("CRAM-MD5", "test", null); + byte[] challenge = ss.evaluateResponse(new byte[0]); + byte[] response = SaslUtil.generateCramMD5ClientResponse(userName, userPassword, challenge); + + AuthenticationResult result = _authenticationManager.authenticate(ss, response); + return result; + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java deleted file mode 100644 index 52b525dd80..0000000000 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/rmi/RMIPasswordAuthenticatorTest.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * - * 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.qpid.server.security.auth.rmi; - -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.security.Principal; -import java.util.regex.Pattern; - -import javax.security.auth.Subject; - -import junit.framework.TestCase; - -import org.apache.qpid.server.model.Broker; -import org.apache.qpid.server.security.SubjectCreator; -import org.apache.qpid.server.security.auth.AuthenticationResult; -import org.apache.qpid.server.security.auth.AuthenticationResult.AuthenticationStatus; -import org.apache.qpid.server.security.auth.SubjectAuthenticationResult; -import org.apache.qpid.server.security.SecurityManager; - -/** - * Tests the RMIPasswordAuthenticator and its collaboration with the AuthenticationManager. - * - */ -public class RMIPasswordAuthenticatorTest extends TestCase -{ - private static final String USERNAME = "guest"; - private static final String PASSWORD = "password"; - - private final Broker _broker = mock(Broker.class); - private final SecurityManager _securityManager = mock(SecurityManager.class); - private final Subject _loginSubject = new Subject(); - private final String[] _credentials = new String[] {USERNAME, PASSWORD}; - - private RMIPasswordAuthenticator _rmipa; - - private SubjectCreator _usernamePasswordOkaySuvjectCreator = createMockSubjectCreator(true, null); - private SubjectCreator _badPasswordSubjectCreator = createMockSubjectCreator(false, null); - - protected void setUp() throws Exception - { - when(_broker.getSecurityManager()).thenReturn(_securityManager); - _rmipa = new RMIPasswordAuthenticator(_broker, new InetSocketAddress(8999)); - } - - /** - * Tests a successful authentication. Ensures that the expected subject is returned. - */ - public void testAuthenticationSuccess() - { - when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(_usernamePasswordOkaySuvjectCreator); - when(_securityManager.accessManagement()).thenReturn(true); - - Subject newSubject = _rmipa.authenticate(_credentials); - assertSame("Subject must be unchanged", _loginSubject, newSubject); - } - - /** - * Tests a unsuccessful authentication. - */ - public void testUsernameOrPasswordInvalid() - { - when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(_badPasswordSubjectCreator); - - try - { - _rmipa.authenticate(_credentials); - fail("Exception not thrown"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.INVALID_CREDENTIALS, se.getMessage()); - } - } - - public void testAuthorisationFailure() - { - when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(_usernamePasswordOkaySuvjectCreator); - when(_securityManager.accessManagement()).thenReturn(false); - - try - { - _rmipa.authenticate(_credentials); - fail("Exception not thrown"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.USER_NOT_AUTHORISED_FOR_MANAGEMENT, se.getMessage()); - } - } - - public void testSubjectCreatorInternalFailure() - { - final Exception mockAuthException = new Exception("Mock Auth system failure"); - SubjectCreator subjectCreator = createMockSubjectCreator(false, mockAuthException); - when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(subjectCreator); - - try - { - _rmipa.authenticate(_credentials); - fail("Exception not thrown"); - } - catch (SecurityException se) - { - assertEquals("Initial cause not found", mockAuthException, se.getCause()); - } - } - - /** - * Tests case where authentication manager is not set. - */ - public void testNullSubjectCreator() throws Exception - { - when(_broker.getSubjectCreator(any(SocketAddress.class))).thenReturn(null); - - try - { - _rmipa.authenticate(_credentials); - fail("SecurityException expected due to lack of authentication manager"); - } - catch (SecurityException se) - { - assertTrue("Unexpected exception message", Pattern.matches("Can't get subject creator for .*:8999", se.getMessage())); - } - } - - /** - * Tests case where arguments are non-Strings.. - */ - public void testWithNonStringArrayArgument() - { - // Test handling of non-string credential's - final Object[] objCredentials = new Object[]{USERNAME, PASSWORD}; - try - { - _rmipa.authenticate(objCredentials); - fail("SecurityException expected due to non string[] credentials"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.SHOULD_BE_STRING_ARRAY, se.getMessage()); - } - } - - /** - * Tests case where there are too many, too few or null arguments. - */ - public void testWithIllegalNumberOfArguments() - { - String[] credentials; - - // Test handling of incorrect number of credentials - try - { - credentials = new String[]{USERNAME, PASSWORD, PASSWORD}; - _rmipa.authenticate(credentials); - fail("SecurityException expected due to supplying wrong number of credentials"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.SHOULD_HAVE_2_ELEMENTS, se.getMessage()); - } - - // Test handling of null credentials - try - { - //send a null array - credentials = null; - _rmipa.authenticate(credentials); - fail("SecurityException expected due to not supplying an array of credentials"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.CREDENTIALS_REQUIRED, se.getMessage()); - } - - try - { - //send a null password - credentials = new String[]{USERNAME, null}; - _rmipa.authenticate(credentials); - fail("SecurityException expected due to sending a null password"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage()); - } - - try - { - //send a null username - credentials = new String[]{null, PASSWORD}; - _rmipa.authenticate(credentials); - fail("SecurityException expected due to sending a null username"); - } - catch (SecurityException se) - { - assertEquals("Unexpected exception message", - RMIPasswordAuthenticator.SHOULD_BE_NON_NULL, se.getMessage()); - } - } - - private SubjectCreator createMockSubjectCreator(final boolean successfulAuth, final Exception exception) - { - SubjectCreator subjectCreator = mock(SubjectCreator.class); - - SubjectAuthenticationResult subjectAuthenticationResult; - - if (exception != null) { - - subjectAuthenticationResult = new SubjectAuthenticationResult( - new AuthenticationResult(AuthenticationStatus.ERROR, exception)); - } - else if (successfulAuth) - { - - subjectAuthenticationResult = new SubjectAuthenticationResult( - new AuthenticationResult(mock(Principal.class)), _loginSubject); - } - else - { - subjectAuthenticationResult = new SubjectAuthenticationResult(new AuthenticationResult(AuthenticationStatus.CONTINUE)); - } - - when(subjectCreator.authenticate(anyString(), anyString())).thenReturn(subjectAuthenticationResult); - - return subjectCreator; - } -} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java index e408fd73d5..3079222b1c 100644 --- a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/CRAMMD5HexInitialiserTest.java @@ -20,88 +20,94 @@ */ package org.apache.qpid.server.security.auth.sasl; -import junit.framework.TestCase; - -import org.apache.qpid.server.security.auth.database.PropertiesPrincipalDatabase; -import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexInitialiser; +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import javax.security.auth.callback.Callback; import javax.security.auth.callback.NameCallback; import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import java.io.IOException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Properties; + +import junit.framework.TestCase; + +import org.apache.qpid.server.security.auth.database.Base64MD5PasswordFilePrincipalDatabase; +import org.apache.qpid.server.security.auth.database.PrincipalDatabase; +import org.apache.qpid.server.security.auth.sasl.crammd5.CRAMMD5HexInitialiser; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.tools.security.Passwd; /** * These tests ensure that the Hex wrapping that the initialiser performs does actually operate when the handle method is called. */ public class CRAMMD5HexInitialiserTest extends TestCase { - public void testHex() - { - //Create User details for testing - String user = "testUser"; - String password = "testPassword"; + private static final String TEST_PASSWORD = "testPassword"; + private static final String TEST_USER = "testUser"; + private File _file; - perform(user, password); - } - - public void testHashedHex() + public void testHashedHex() throws Exception { - //Create User details for testing - String user = "testUser"; - String password = "testPassword"; - - //Create a hashed password that we then attempt to put through the call back mechanism. - try - { - password = new String(MessageDigest.getInstance("MD5").digest(password.getBytes())); - } - catch (NoSuchAlgorithmException e) - { - fail(e.getMessage()); - } - - perform(user, password); + perform(TEST_USER, getHash(TEST_PASSWORD)); } - public void perform(String user, String password) + public void perform(String user, char[] password) throws Exception { CRAMMD5HexInitialiser initialiser = new CRAMMD5HexInitialiser(); - //Use properties to create a PrincipalDatabase - Properties users = new Properties(); - users.put(user, password); - - PropertiesPrincipalDatabase db = new PropertiesPrincipalDatabase(users); - + PrincipalDatabase db = new Base64MD5PasswordFilePrincipalDatabase(); + db.open(_file); initialiser.initialise(db); - //setup the callbacks PasswordCallback passwordCallback = new PasswordCallback("password:", false); NameCallback usernameCallback = new NameCallback("user:", user); Callback[] callbacks = new Callback[]{usernameCallback, passwordCallback}; - //Check the - try + assertNull("The password was not null before the handle call.", passwordCallback.getPassword()); + initialiser.getCallbackHandler().handle(callbacks); + + assertArrayEquals(toHex(password), passwordCallback.getPassword()); + } + + public void setUp() throws Exception + { + super.setUp(); + _file = TestFileUtils.createTempFile(this, "password-file", new Passwd().getOutput(TEST_USER , TEST_PASSWORD)); + } + + public void tearDown() throws Exception + { + if (_file != null) { - assertNull("The password was not null before the handle call.", passwordCallback.getPassword()); - initialiser.getCallbackHandler().handle(callbacks); + _file.delete(); } - catch (IOException e) + super.tearDown(); + } + + private char[] getHash(String text) throws NoSuchAlgorithmException, UnsupportedEncodingException + { + + byte[] data = text.getBytes("utf-8"); + + MessageDigest md = MessageDigest.getInstance("MD5"); + + for (byte b : data) { - fail(e.getMessage()); + md.update(b); } - catch (UnsupportedCallbackException e) + + byte[] digest = md.digest(); + + char[] hash = new char[digest.length]; + + int index = 0; + for (byte b : digest) { - fail(e.getMessage()); + hash[index++] = (char) b; } - //Hex the password we initialised with and compare it with the passwordCallback - assertArrayEquals(toHex(password.toCharArray()), passwordCallback.getPassword()); + return hash; } private void assertArrayEquals(char[] expected, char[] actual) @@ -135,4 +141,5 @@ public class CRAMMD5HexInitialiserTest extends TestCase return hex; } + } diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslUtil.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslUtil.java new file mode 100644 index 0000000000..251ebc4c81 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/security/auth/sasl/SaslUtil.java @@ -0,0 +1,85 @@ +/* + * + * 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.qpid.server.security.auth.sasl; + +import java.security.MessageDigest; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +public class SaslUtil +{ + + private static byte SEPARATOR = 0; + + public static byte[] generatePlainClientResponse(String userName, String userPassword) throws Exception + { + byte[] password = userPassword.getBytes("UTF8"); + byte user[] = userName.getBytes("UTF8"); + byte response[] = new byte[password.length + user.length + 2]; + int size = 0; + response[size++] = SEPARATOR; + System.arraycopy(user, 0, response, size, user.length); + size += user.length; + response[size++] = SEPARATOR; + System.arraycopy(password, 0, response, size, password.length); + return response; + } + + public static byte[] generateCramMD5HexClientResponse(String userName, String userPassword, byte[] challengeBytes) + throws Exception + { + String macAlgorithm = "HmacMD5"; + byte[] digestedPasswordBytes = MessageDigest.getInstance("MD5").digest(userPassword.getBytes("UTF-8")); + byte[] hexEncodedDigestedPasswordBytes = toHex(digestedPasswordBytes).getBytes("UTF-8"); + Mac mac = Mac.getInstance(macAlgorithm); + mac.init(new SecretKeySpec(hexEncodedDigestedPasswordBytes, macAlgorithm)); + final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes); + String responseAsString = userName + " " + toHex(messageAuthenticationCode); + return responseAsString.getBytes(); + } + + public static byte[] generateCramMD5ClientResponse(String userName, String userPassword, byte[] challengeBytes) + throws Exception + { + String macAlgorithm = "HmacMD5"; + Mac mac = Mac.getInstance(macAlgorithm); + mac.init(new SecretKeySpec(userPassword.getBytes("UTF-8"), macAlgorithm)); + final byte[] messageAuthenticationCode = mac.doFinal(challengeBytes); + String responseAsString = userName + " " + toHex(messageAuthenticationCode); + return responseAsString.getBytes(); + } + + public static String toHex(byte[] data) + { + StringBuffer hash = new StringBuffer(); + for (int i = 0; i < data.length; i++) + { + String hex = Integer.toHexString(0xFF & data[i]); + if (hex.length() == 1) + { + hash.append('0'); + } + hash.append(hex); + } + return hash.toString(); + } +} diff --git a/qpid/java/broker/src/test/java/org/apache/qpid/server/util/StringUtilTest.java b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/StringUtilTest.java new file mode 100644 index 0000000000..93b4172792 --- /dev/null +++ b/qpid/java/broker/src/test/java/org/apache/qpid/server/util/StringUtilTest.java @@ -0,0 +1,38 @@ +package org.apache.qpid.server.util; + +import org.apache.qpid.server.util.StringUtil; +import org.apache.qpid.test.utils.QpidTestCase; + +public class StringUtilTest extends QpidTestCase +{ + private StringUtil _util; + + @Override + public void setUp() throws Exception + { + super.setUp(); + _util = new StringUtil(); + } + + public void testRandomAlphaNumericStringInt() + { + String password = _util.randomAlphaNumericString(10); + assertEquals("Unexpected password string length", 10, password.length()); + assertCharacters(password); + } + + private void assertCharacters(String password) + { + String numbers = "0123456789"; + String letters = "abcdefghijklmnopqrstuvwxwy"; + String others = "_-"; + String expectedCharacters = (numbers + letters + letters.toUpperCase() + others); + char[] chars = password.toCharArray(); + for (int i = 0; i < chars.length; i++) + { + char ch = chars[i]; + assertTrue("Unexpected character " + ch, expectedCharacters.indexOf(ch) != -1); + } + } + +} -- cgit v1.2.1