001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.core;
028 import org.opends.messages.Message;
029
030
031
032 import java.lang.reflect.Method;
033 import java.util.ArrayList;
034 import java.util.Iterator;
035 import java.util.List;
036 import java.util.concurrent.ConcurrentHashMap;
037
038 import org.opends.server.admin.ClassPropertyDefinition;
039 import org.opends.server.admin.server.ConfigurationAddListener;
040 import org.opends.server.admin.server.ConfigurationChangeListener;
041 import org.opends.server.admin.server.ConfigurationDeleteListener;
042 import org.opends.server.admin.std.meta.SASLMechanismHandlerCfgDefn;
043 import org.opends.server.admin.std.server.SASLMechanismHandlerCfg;
044 import org.opends.server.admin.std.server.RootCfg;
045 import org.opends.server.admin.server.ServerManagementContext;
046 import org.opends.server.api.SASLMechanismHandler;
047 import org.opends.server.config.ConfigException;
048 import org.opends.server.types.ConfigChangeResult;
049 import org.opends.server.types.DN;
050
051
052 import org.opends.server.types.InitializationException;
053 import org.opends.server.types.ResultCode;
054
055 import static org.opends.messages.ConfigMessages.*;
056
057 import static org.opends.server.util.StaticUtils.*;
058 import org.opends.server.loggers.ErrorLogger;
059
060
061 /**
062 * This class defines a utility that will be used to manage the set of SASL
063 * mechanism handlers defined in the Directory Server. It will initialize the
064 * handlers when the server starts, and then will manage any additions,
065 * removals, or modifications to any SASL mechanism handlers while the server is
066 * running.
067 */
068 public class SASLConfigManager implements
069 ConfigurationChangeListener<SASLMechanismHandlerCfg>,
070 ConfigurationAddListener<SASLMechanismHandlerCfg>,
071 ConfigurationDeleteListener<SASLMechanismHandlerCfg>
072
073 {
074 // A mapping between the DNs of the config entries and the
075 // associated SASL
076 // mechanism handlers.
077 private ConcurrentHashMap<DN,SASLMechanismHandler> handlers;
078
079
080
081 /**
082 * Creates a new instance of this SASL mechanism handler config manager.
083 */
084 public SASLConfigManager()
085 {
086 handlers = new ConcurrentHashMap<DN,SASLMechanismHandler>();
087 }
088
089
090
091 /**
092 * Initializes all SASL mechanism hanlders currently defined in the Directory
093 * Server configuration. This should only be called at Directory Server
094 * startup.
095 *
096 * @throws ConfigException If a configuration problem causes the SASL
097 * mechanism handler initialization process to fail.
098 *
099 * @throws InitializationException If a problem occurs while initializing
100 * the SASL mechanism handlers that is not
101 * related to the server configuration.
102 */
103 public void initializeSASLMechanismHandlers()
104 throws ConfigException, InitializationException
105 {
106 // Get the root configuration object.
107 ServerManagementContext managementContext =
108 ServerManagementContext.getInstance();
109 RootCfg rootConfiguration =
110 managementContext.getRootConfiguration();
111
112
113 // Register as an add and delete listener with the root configuration so we
114 // can be notified if any SASL mechanism handler entries are added or
115 // removed.
116 rootConfiguration.addSASLMechanismHandlerAddListener(this);
117 rootConfiguration.addSASLMechanismHandlerDeleteListener(this);
118
119
120 //Initialize the existing SASL mechanism handlers.
121 for (String handlerName : rootConfiguration.listSASLMechanismHandlers())
122 {
123 SASLMechanismHandlerCfg handlerConfiguration =
124 rootConfiguration.getSASLMechanismHandler(handlerName);
125 handlerConfiguration.addChangeListener(this);
126
127 if (handlerConfiguration.isEnabled())
128 {
129 String className = handlerConfiguration.getJavaClass();
130 try
131 {
132 SASLMechanismHandler handler = loadHandler(className,
133 handlerConfiguration,
134 true);
135 handlers.put(handlerConfiguration.dn(), handler);
136 }
137 catch (InitializationException ie)
138 {
139 ErrorLogger.logError(ie.getMessageObject());
140 continue;
141 }
142 }
143 }
144 }
145
146
147
148 /**
149 * {@inheritDoc}
150 */
151 public boolean isConfigurationAddAcceptable(
152 SASLMechanismHandlerCfg configuration,
153 List<Message> unacceptableReasons)
154 {
155 if (configuration.isEnabled())
156 {
157 // Get the name of the class and make sure we can instantiate it as a SASL
158 // mechanism handler.
159 String className = configuration.getJavaClass();
160 try
161 {
162 loadHandler(className, configuration, false);
163 }
164 catch (InitializationException ie)
165 {
166 unacceptableReasons.add(ie.getMessageObject());
167 return false;
168 }
169 }
170
171 // If we've gotten here, then it's fine.
172 return true;
173 }
174
175
176
177 /**
178 * {@inheritDoc}
179 */
180 public ConfigChangeResult applyConfigurationAdd(
181 SASLMechanismHandlerCfg configuration)
182 {
183 ResultCode resultCode = ResultCode.SUCCESS;
184 boolean adminActionRequired = false;
185 ArrayList<Message> messages = new ArrayList<Message>();
186
187 configuration.addChangeListener(this);
188
189 if (! configuration.isEnabled())
190 {
191 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
192 }
193
194 SASLMechanismHandler handler = null;
195
196 // Get the name of the class and make sure we can instantiate it as a SASL
197 // mechanism handler.
198 String className = configuration.getJavaClass();
199 try
200 {
201 handler = loadHandler(className, configuration, true);
202 }
203 catch (InitializationException ie)
204 {
205 if (resultCode == ResultCode.SUCCESS)
206 {
207 resultCode = DirectoryServer.getServerErrorResultCode();
208 }
209
210 messages.add(ie.getMessageObject());
211 }
212
213 if (resultCode == ResultCode.SUCCESS)
214 {
215 handlers.put(configuration.dn(), handler);
216 }
217
218 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
219 }
220
221
222
223 /**
224 * {@inheritDoc}
225 */
226 public boolean isConfigurationDeleteAcceptable(
227 SASLMechanismHandlerCfg configuration,
228 List<Message> unacceptableReasons)
229 {
230 // FIXME -- We should try to perform some check to determine whether the
231 // SASL mechanism handler is in use.
232 return true;
233 }
234
235
236
237 /**
238 * {@inheritDoc}
239 */
240 public ConfigChangeResult applyConfigurationDelete(
241 SASLMechanismHandlerCfg configuration)
242 {
243 ResultCode resultCode = ResultCode.SUCCESS;
244 boolean adminActionRequired = false;
245 ArrayList<Message> messages = new ArrayList<Message>();
246
247 SASLMechanismHandler handler = handlers.remove(configuration.dn());
248 if (handler != null)
249 {
250 handler.finalizeSASLMechanismHandler();
251 }
252
253 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
254 }
255
256
257
258 /**
259 * {@inheritDoc}
260 */
261 public boolean isConfigurationChangeAcceptable(
262 SASLMechanismHandlerCfg configuration,
263 List<Message> unacceptableReasons)
264 {
265 if (configuration.isEnabled())
266 {
267 // Get the name of the class and make sure we can instantiate it as a SASL
268 // mechanism handler.
269 String className = configuration.getJavaClass();
270 try
271 {
272 loadHandler(className, configuration, false);
273 }
274 catch (InitializationException ie)
275 {
276 unacceptableReasons.add(ie.getMessageObject());
277 return false;
278 }
279 }
280
281 // If we've gotten here, then it's fine.
282 return true;
283 }
284
285
286
287 /**
288 * {@inheritDoc}
289 */
290 public ConfigChangeResult applyConfigurationChange(
291 SASLMechanismHandlerCfg configuration)
292 {
293 ResultCode resultCode = ResultCode.SUCCESS;
294 boolean adminActionRequired = false;
295 ArrayList<Message> messages = new ArrayList<Message>();
296
297
298 // Get the existing handler if it's already enabled.
299 SASLMechanismHandler existingHandler = handlers.get(configuration.dn());
300
301
302 // If the new configuration has the handler disabled, then disable it if it
303 // is enabled, or do nothing if it's already disabled.
304 if (! configuration.isEnabled())
305 {
306 if (existingHandler != null)
307 {
308 SASLMechanismHandler handler = handlers.remove(configuration.dn());
309 if (handler != null)
310 {
311 handler.finalizeSASLMechanismHandler();
312 }
313 }
314
315 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
316 }
317
318
319 // Get the class for the SASL handler. If the handler is already enabled,
320 // then we shouldn't do anything with it although if the class has changed
321 // then we'll at least need to indicate that administrative action is
322 // required. If the handler is disabled, then instantiate the class and
323 // initialize and register it as a SASL mechanism handler.
324 String className = configuration.getJavaClass();
325 if (existingHandler != null)
326 {
327 if (! className.equals(existingHandler.getClass().getName()))
328 {
329 adminActionRequired = true;
330 }
331
332 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
333 }
334
335 SASLMechanismHandler handler = null;
336 try
337 {
338 handler = loadHandler(className, configuration, true);
339 }
340 catch (InitializationException ie)
341 {
342 if (resultCode == ResultCode.SUCCESS)
343 {
344 resultCode = DirectoryServer.getServerErrorResultCode();
345 }
346
347 messages.add(ie.getMessageObject());
348 }
349
350 if (resultCode == ResultCode.SUCCESS)
351 {
352 handlers.put(configuration.dn(), handler);
353 }
354
355 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
356 }
357
358
359
360 /**
361 * Loads the specified class, instantiates it as a SASL mechanism hanlder, and
362 * optionally initializes that instance.
363 *
364 * @param className The fully-qualified name of the SASL mechanism
365 * handler class to load, instantiate, and initialize.
366 * @param configuration The configuration to use to initialize the handler.
367 * It must not be {@code null}.
368 * @param initialize Indicates whether the SASL mechanism handler
369 * instance should be initialized.
370 *
371 * @return The possibly initialized SASL mechanism handler.
372 *
373 * @throws InitializationException If a problem occurred while attempting to
374 * initialize the SASL mechanism handler.
375 */
376 private SASLMechanismHandler loadHandler(String className,
377 SASLMechanismHandlerCfg
378 configuration,
379 boolean initialize)
380 throws InitializationException
381 {
382 try
383 {
384 SASLMechanismHandlerCfgDefn definition =
385 SASLMechanismHandlerCfgDefn.getInstance();
386 ClassPropertyDefinition propertyDefinition =
387 definition.getJavaClassPropertyDefinition();
388 Class<? extends SASLMechanismHandler> handlerClass =
389 propertyDefinition.loadClass(className, SASLMechanismHandler.class);
390 SASLMechanismHandler handler = handlerClass.newInstance();
391
392 if (initialize)
393 {
394 Method method = handler.getClass().getMethod(
395 "initializeSASLMechanismHandler",
396 configuration.configurationClass());
397 method.invoke(handler, configuration);
398 }
399 else
400 {
401 Method method =
402 handler.getClass().getMethod("isConfigurationAcceptable",
403 SASLMechanismHandlerCfg.class,
404 List.class);
405
406 List<Message> unacceptableReasons = new ArrayList<Message>();
407 Boolean acceptable = (Boolean) method.invoke(handler, configuration,
408 unacceptableReasons);
409 if (! acceptable)
410 {
411 StringBuilder buffer = new StringBuilder();
412 if (! unacceptableReasons.isEmpty())
413 {
414 Iterator<Message> iterator = unacceptableReasons.iterator();
415 buffer.append(iterator.next());
416 while (iterator.hasNext())
417 {
418 buffer.append(". ");
419 buffer.append(iterator.next());
420 }
421 }
422
423 Message message = ERR_CONFIG_SASL_CONFIG_NOT_ACCEPTABLE.get(
424 String.valueOf(configuration.dn()), buffer.toString());
425 throw new InitializationException(message);
426 }
427 }
428
429 return handler;
430 }
431 catch (Exception e)
432 {
433 Message message = ERR_CONFIG_SASL_INITIALIZATION_FAILED.
434 get(className, String.valueOf(configuration.dn()),
435 stackTraceToSingleLineString(e));
436 throw new InitializationException(message, e);
437 }
438 }
439 }
440