001 /*
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements. See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership. The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied. See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 *
020 */
021 package org.apache.qpid.client.security;
022
023 import org.apache.qpid.util.FileUtils;
024
025 import org.slf4j.Logger;
026 import org.slf4j.LoggerFactory;
027
028 import java.io.IOException;
029 import java.io.InputStream;
030 import java.util.Enumeration;
031 import java.util.HashMap;
032 import java.util.Map;
033 import java.util.Properties;
034
035 /**
036 * CallbackHandlerRegistry is a registry for call back handlers for user authentication and interaction during user
037 * authentication. It is capable of reading its configuration from a properties file containing call back handler
038 * implementing class names for different SASL mechanism names. Instantiating this registry also has the effect of
039 * configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}.
040 *
041 * <p/>The callback configuration should be specified in a properties file, refered to by the System property
042 * "amp.callbackhandler.properties". The format of the properties file is:
043 *
044 * <p/><pre>
045 * CallbackHanlder.mechanism=fully.qualified.class.name
046 * </pre>
047 *
048 * <p/>Where mechanism is an IANA-registered mechanism name and the fully qualified class name refers to a
049 * class that implements org.apache.qpid.client.security.AMQCallbackHanlder and provides a call back handler for the
050 * specified mechanism.
051 *
052 * <p><table id="crc"><caption>CRC Card</caption>
053 * <tr><th> Responsibilities <th> Collaborations
054 * <tr><td> Parse callback properties.
055 * <tr><td> Provide mapping from SASL mechanisms to callback implementations.
056 * </table>
057 */
058 public class CallbackHandlerRegistry
059 {
060 private static final Logger _logger = LoggerFactory.getLogger(CallbackHandlerRegistry.class);
061
062 /** The name of the system property that holds the name of the callback handler properties file. */
063 private static final String FILE_PROPERTY = "amq.callbackhandler.properties";
064
065 /** The default name of the callback handler properties resource. */
066 public static final String DEFAULT_RESOURCE_NAME = "org/apache/qpid/client/security/CallbackHandlerRegistry.properties";
067
068 /** A static reference to the singleton instance of this registry. */
069 private static CallbackHandlerRegistry _instance = new CallbackHandlerRegistry();
070
071 /** Holds a map from SASL mechanism names to call back handlers. */
072 private Map<String, Class> _mechanismToHandlerClassMap = new HashMap<String, Class>();
073
074 /** Holds a space delimited list of mechanisms that callback handlers exist for. */
075 private String _mechanisms;
076
077 /**
078 * Gets the singleton instance of this registry.
079 *
080 * @return The singleton instance of this registry.
081 */
082 public static CallbackHandlerRegistry getInstance()
083 {
084 return _instance;
085 }
086
087 /**
088 * Gets the callback handler class for a given SASL mechanism name.
089 *
090 * @param mechanism The SASL mechanism name.
091 *
092 * @return The callback handler class for the mechanism, or null if none is configured for that mechanism.
093 */
094 public Class getCallbackHandlerClass(String mechanism)
095 {
096 return (Class) _mechanismToHandlerClassMap.get(mechanism);
097 }
098
099 /**
100 * Gets a space delimited list of supported SASL mechanisms.
101 *
102 * @return A space delimited list of supported SASL mechanisms.
103 */
104 public String getMechanisms()
105 {
106 return _mechanisms;
107 }
108
109 /**
110 * Creates the call back handler registry from its configuration resource or file. This also has the side effect
111 * of configuring and registering the SASL client factory implementations using {@link DynamicSaslRegistrar}.
112 */
113 private CallbackHandlerRegistry()
114 {
115 // Register any configured SASL client factories.
116 DynamicSaslRegistrar.registerSaslProviders();
117
118 String filename = System.getProperty(FILE_PROPERTY);
119 InputStream is =
120 FileUtils.openFileOrDefaultResource(filename, DEFAULT_RESOURCE_NAME,
121 CallbackHandlerRegistry.class.getClassLoader());
122
123 try
124 {
125 Properties props = new Properties();
126 props.load(is);
127 parseProperties(props);
128 _logger.info("Callback handlers available for SASL mechanisms: " + _mechanisms);
129 }
130 catch (IOException e)
131 {
132 _logger.error("Error reading properties: " + e, e);
133 }
134 finally
135 {
136 if (is != null)
137 {
138 try
139 {
140 is.close();
141
142 }
143 catch (IOException e)
144 {
145 _logger.error("Unable to close properties stream: " + e, e);
146 }
147 }
148 }
149 }
150
151 /*private InputStream openPropertiesInputStream(String filename)
152 {
153 boolean useDefault = true;
154 InputStream is = null;
155 if (filename != null)
156 {
157 try
158 {
159 is = new BufferedInputStream(new FileInputStream(new File(filename)));
160 useDefault = false;
161 }
162 catch (FileNotFoundException e)
163 {
164 _logger.error("Unable to read from file " + filename + ": " + e, e);
165 }
166 }
167
168 if (useDefault)
169 {
170 is = CallbackHandlerRegistry.class.getResourceAsStream(DEFAULT_RESOURCE_NAME);
171 }
172
173 return is;
174 }*/
175
176 /**
177 * Scans the specified properties as a mapping from IANA registered SASL mechanism to call back handler
178 * implementations, that provide the necessary call back handling for obtaining user log in credentials
179 * during authentication for the specified mechanism, and builds a map from mechanism names to handler
180 * classes.
181 *
182 * @param props
183 */
184 private void parseProperties(Properties props)
185 {
186 Enumeration e = props.propertyNames();
187 while (e.hasMoreElements())
188 {
189 String propertyName = (String) e.nextElement();
190 int period = propertyName.indexOf(".");
191 if (period < 0)
192 {
193 _logger.warn("Unable to parse property " + propertyName + " when configuring SASL providers");
194
195 continue;
196 }
197
198 String mechanism = propertyName.substring(period + 1);
199 String className = props.getProperty(propertyName);
200 Class clazz = null;
201 try
202 {
203 clazz = Class.forName(className);
204 if (!AMQCallbackHandler.class.isAssignableFrom(clazz))
205 {
206 _logger.warn("SASL provider " + clazz + " does not implement " + AMQCallbackHandler.class
207 + ". Skipping");
208
209 continue;
210 }
211
212 _mechanismToHandlerClassMap.put(mechanism, clazz);
213 if (_mechanisms == null)
214 {
215 _mechanisms = mechanism;
216 }
217 else
218 {
219 // one time cost
220 _mechanisms = _mechanisms + " " + mechanism;
221 }
222 }
223 catch (ClassNotFoundException ex)
224 {
225 _logger.warn("Unable to load class " + className + ". Skipping that SASL provider");
226
227 continue;
228 }
229 }
230 }
231 }
|