/* * * 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.client.security; import org.apache.qpid.util.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * CallbackHandlerRegistry is a registry for call back handlers for user authentication and interaction during user * authentication. It is capable of reading its configuration from a properties file containing call back handler * implementing class names for different SASL mechanism names. Instantiating this registry also has the effect of * configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}. * *

The callback configuration should be specified in a properties file, refered to by the System property * "amp.callbackhandler.properties". The format of the properties file is: * *

 * CallbackHanlder.mechanism=fully.qualified.class.name
 * 
* *

Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a * class that implements org.apache.qpid.client.security.AMQCallbackHanlder and provides a call back handler for the * specified mechanism. * *

*
CRC Card
Responsibilities Collaborations *
Parse callback properties. *
Provide mapping from SASL mechanisms to callback implementations. *
*/ public class CallbackHandlerRegistry { private static final Logger _logger = LoggerFactory.getLogger(CallbackHandlerRegistry.class); /** The name of the system property that holds the name of the callback handler properties file. */ private static final String FILE_PROPERTY = "amq.callbackhandler.properties"; /** The default name of the callback handler properties resource. */ public static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/client/security/CallbackHandlerRegistry.properties"; /** A static reference to the singleton instance of this registry. */ private static CallbackHandlerRegistry _instance = new CallbackHandlerRegistry(); /** Holds a map from SASL mechanism names to call back handlers. */ private Map _mechanismToHandlerClassMap = new HashMap(); /** Holds a space delimited list of mechanisms that callback handlers exist for. */ private String _mechanisms; /** * Gets the singleton instance of this registry. * * @return The singleton instance of this registry. */ public static CallbackHandlerRegistry getInstance() { return _instance; } /** * Gets the callback handler class for a given SASL mechanism name. * * @param mechanism The SASL mechanism name. * * @return The callback handler class for the mechanism, or null if none is configured for that mechanism. */ public Class getCallbackHandlerClass(String mechanism) { return (Class) _mechanismToHandlerClassMap.get(mechanism); } /** * Gets a space delimited list of supported SASL mechanisms. * * @return A space delimited list of supported SASL mechanisms. */ public String getMechanisms() { return _mechanisms; } /** * Creates the call back handler registry from its configuration resource or file. This also has the side effect * of configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}. */ private CallbackHandlerRegistry() { // Register any configured SASL client factories. DynamicSaslRegistrar.registerSaslProviders(); String filename = System.getProperty(FILE_PROPERTY); InputStream is = FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME, CallbackHandlerRegistry.class.getClassLoader()); try { Properties props = new Properties(); props.load(is); parseProperties(props); _logger.info("Callback handlers available for SASL mechanisms: " + _mechanisms); } catch (IOException e) { _logger.error("Error reading properties: " + e, e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { _logger.error("Unable to close properties stream: " + e, e); } } } } /*private InputStream openPropertiesInputStream(String filename) { boolean useDefault = true; InputStream is = null; if (filename != null) { try { is = new BufferedInputStream(new FileInputStream(new File(filename))); useDefault = false; } catch (FileNotFoundException e) { _logger.error("Unable to read from file " + filename + ": " + e, e); } } if (useDefault) { is = CallbackHandlerRegistry.class.getResourceAsStream(DEFAULT_RESOURCE_NAME); } return is; }*/ /** * Scans the specified properties as a mapping from IANA registered SASL mechanism to call back handler * implementations, that provide the necessary call back handling for obtaining user log in credentials * during authentication for the specified mechanism, and builds a map from mechanism names to handler * classes. * * @param props */ private void parseProperties(Properties props) { Enumeration e = props.propertyNames(); while (e.hasMoreElements()) { String propertyName = (String) e.nextElement(); int period = propertyName.indexOf("."); if (period < 0) { _logger.warn("Unable to parse property " + propertyName + " when configuring SASL providers"); continue; } String mechanism = propertyName.substring(period + 1); String className = props.getProperty(propertyName); Class clazz = null; try { clazz = Class.forName(className); if (!AMQCallbackHandler.class.isAssignableFrom(clazz)) { _logger.warn("SASL provider " + clazz + " does not implement " + AMQCallbackHandler.class + ". Skipping"); continue; } _mechanismToHandlerClassMap.put(mechanism, clazz); if (_mechanisms == null) { _mechanisms = mechanism; } else { // one time cost _mechanisms = _mechanisms + " " + mechanism; } } catch (ClassNotFoundException ex) { _logger.warn("Unable to load class " + className + ". Skipping that SASL provider"); continue; } } } }