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.server.ServerManagementContext;
043 import org.opends.server.admin.std.meta.PasswordGeneratorCfgDefn;
044 import org.opends.server.admin.std.server.PasswordGeneratorCfg;
045 import org.opends.server.admin.std.server.RootCfg;
046 import org.opends.server.api.PasswordGenerator;
047 import org.opends.server.config.ConfigException;
048 import org.opends.server.types.ConfigChangeResult;
049 import org.opends.server.types.DN;
050 import org.opends.server.types.InitializationException;
051 import org.opends.server.types.ResultCode;
052
053 import static org.opends.messages.ConfigMessages.*;
054
055 import static org.opends.server.loggers.ErrorLogger.*;
056 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString;
057
058
059
060 /**
061 * This class defines a utility that will be used to manage the set of password
062 * generators defined in the Directory Server. It will initialize the
063 * generators when the server starts, and then will manage any additions,
064 * removals, or modifications to any password generators while the server is
065 * running.
066 */
067 public class PasswordGeneratorConfigManager
068 implements ConfigurationAddListener<PasswordGeneratorCfg>,
069 ConfigurationDeleteListener<PasswordGeneratorCfg>,
070 ConfigurationChangeListener<PasswordGeneratorCfg>
071 {
072
073 // A mapping between the DNs of the config entries and the associated password
074 // generators.
075 private ConcurrentHashMap<DN,PasswordGenerator> passwordGenerators;
076
077
078 /**
079 * Creates a new instance of this password generator config manager.
080 */
081 public PasswordGeneratorConfigManager()
082 {
083 passwordGenerators = new ConcurrentHashMap<DN,PasswordGenerator>();
084 }
085
086
087
088 /**
089 * Initializes all password generators currently defined in the Directory
090 * Server configuration. This should only be called at Directory Server
091 * startup.
092 *
093 * @throws ConfigException If a configuration problem causes the password
094 * generator initialization process to fail.
095 *
096 * @throws InitializationException If a problem occurs while initializing
097 * the password generators that is not
098 * related to the server configuration.
099 */
100 public void initializePasswordGenerators()
101 throws ConfigException, InitializationException
102 {
103 // Get the root configuration object.
104 ServerManagementContext managementContext =
105 ServerManagementContext.getInstance();
106 RootCfg rootConfiguration =
107 managementContext.getRootConfiguration();
108
109 // Register as an add and delete listener with the root configuration so we
110 // can be notified if any password generator entries are added or removed.
111 rootConfiguration.addPasswordGeneratorAddListener(this);
112 rootConfiguration.addPasswordGeneratorDeleteListener(this);
113
114
115 //Initialize the existing password generators.
116 for (String generatorName : rootConfiguration.listPasswordGenerators())
117 {
118 PasswordGeneratorCfg generatorConfiguration =
119 rootConfiguration.getPasswordGenerator(generatorName);
120 generatorConfiguration.addChangeListener(this);
121
122 if (generatorConfiguration.isEnabled())
123 {
124 String className = generatorConfiguration.getJavaClass();
125 try
126 {
127 PasswordGenerator<? extends PasswordGeneratorCfg>
128 generator = loadGenerator(className, generatorConfiguration,
129 true);
130 passwordGenerators.put(generatorConfiguration.dn(), generator);
131 DirectoryServer.registerPasswordGenerator(generatorConfiguration.dn(),
132 generator);
133 }
134 catch (InitializationException ie)
135 {
136 logError(ie.getMessageObject());
137 continue;
138 }
139 }
140 }
141 }
142
143 /**
144 * {@inheritDoc}
145 */
146 public boolean isConfigurationChangeAcceptable(
147 PasswordGeneratorCfg configuration,
148 List<Message> unacceptableReasons)
149 {
150 if (configuration.isEnabled())
151 {
152 // Get the name of the class and make sure we can instantiate it as a
153 // password generator.
154 String className = configuration.getJavaClass();
155 try
156 {
157 loadGenerator(className, configuration, false);
158 }
159 catch (InitializationException ie)
160 {
161 unacceptableReasons.add(ie.getMessageObject());
162 return false;
163 }
164 }
165
166 // If we've gotten here, then it's fine.
167 return true;
168 }
169
170
171 /**
172 * {@inheritDoc}
173 */
174 public ConfigChangeResult applyConfigurationChange(
175 PasswordGeneratorCfg configuration)
176 {
177 ResultCode resultCode = ResultCode.SUCCESS;
178 boolean adminActionRequired = false;
179 ArrayList<Message> messages = new ArrayList<Message>();
180
181
182 // Get the existing generator if it's already enabled.
183 PasswordGenerator existingGenerator =
184 passwordGenerators.get(configuration.dn());
185
186
187 // If the new configuration has the generator disabled, then disable it if
188 // it is enabled, or do nothing if it's already disabled.
189 if (! configuration.isEnabled())
190 {
191 if (existingGenerator != null)
192 {
193 DirectoryServer.deregisterPasswordGenerator(configuration.dn());
194
195 PasswordGenerator passwordGenerator =
196 passwordGenerators.remove(configuration.dn());
197 if (passwordGenerator != null)
198 {
199 passwordGenerator.finalizePasswordGenerator();
200 }
201 }
202
203 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
204 }
205
206
207 // Get the class for the password generator. If the generator is already
208 // enabled, then we shouldn't do anything with it although if the class has
209 // changed then we'll at least need to indicate that administrative action
210 // is required. If the generator is disabled, then instantiate the class
211 // and initialize and register it as a password generator.
212 String className = configuration.getJavaClass();
213 if (existingGenerator != null)
214 {
215 if (! className.equals(existingGenerator.getClass().getName()))
216 {
217 adminActionRequired = true;
218 }
219
220 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
221 }
222
223 PasswordGenerator<? extends PasswordGeneratorCfg>
224 passwordGenerator = null;
225 try
226 {
227 passwordGenerator = loadGenerator(className, configuration, true);
228 }
229 catch (InitializationException ie)
230 {
231 if (resultCode == ResultCode.SUCCESS)
232 {
233 resultCode = DirectoryServer.getServerErrorResultCode();
234 }
235
236 messages.add(ie.getMessageObject());
237 }
238
239 if (resultCode == ResultCode.SUCCESS)
240 {
241 passwordGenerators.put(configuration.dn(), passwordGenerator);
242 DirectoryServer.registerPasswordGenerator(configuration.dn(),
243 passwordGenerator);
244 }
245
246 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
247 }
248 /**
249 * {@inheritDoc}
250 */
251 public boolean isConfigurationAddAcceptable(
252 PasswordGeneratorCfg configuration,
253 List<Message> unacceptableReasons)
254 {
255 if (configuration.isEnabled())
256 {
257 // Get the name of the class and make sure we can instantiate it as a
258 // password generator.
259 String className = configuration.getJavaClass();
260 try
261 {
262 loadGenerator(className, configuration, false);
263 }
264 catch (InitializationException ie)
265 {
266 unacceptableReasons.add(ie.getMessageObject());
267 return false;
268 }
269 }
270
271 // If we've gotten here, then it's fine.
272 return true;
273 }
274
275
276 /**
277 * {@inheritDoc}
278 */
279 public ConfigChangeResult applyConfigurationAdd(
280 PasswordGeneratorCfg configuration)
281 {
282 ResultCode resultCode = ResultCode.SUCCESS;
283 boolean adminActionRequired = false;
284 ArrayList<Message> messages = new ArrayList<Message>();
285
286 configuration.addChangeListener(this);
287
288 if (! configuration.isEnabled())
289 {
290 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
291 }
292
293 PasswordGenerator<? extends PasswordGeneratorCfg>
294 passwordGenerator = null;
295
296 // Get the name of the class and make sure we can instantiate it as a
297 // password generator.
298 String className = configuration.getJavaClass();
299 try
300 {
301 passwordGenerator = loadGenerator(className, configuration, true);
302 }
303 catch (InitializationException ie)
304 {
305 if (resultCode == ResultCode.SUCCESS)
306 {
307 resultCode = DirectoryServer.getServerErrorResultCode();
308 }
309
310 messages.add(ie.getMessageObject());
311 }
312
313 if (resultCode == ResultCode.SUCCESS)
314 {
315 passwordGenerators.put(configuration.dn(), passwordGenerator);
316 DirectoryServer.registerPasswordGenerator(configuration.dn(),
317 passwordGenerator);
318 }
319
320 return new ConfigChangeResult(resultCode, adminActionRequired, messages);
321 }
322
323 /**
324 * {@inheritDoc}
325 */
326 public boolean isConfigurationDeleteAcceptable(
327 PasswordGeneratorCfg configuration, List<Message> unacceptableReasons)
328 {
329 // A delete should always be acceptable, so just return true.
330 return true;
331 }
332
333
334 /**
335 * {@inheritDoc}
336 */
337 public ConfigChangeResult applyConfigurationDelete(
338 PasswordGeneratorCfg configuration)
339 {
340 ResultCode resultCode = ResultCode.SUCCESS;
341 boolean adminActionRequired = false;
342
343
344 // See if the entry is registered as a password generator. If so,
345 // deregister it and stop the generator.
346 PasswordGenerator generator = passwordGenerators.remove(configuration.dn());
347 if (generator != null)
348 {
349 DirectoryServer.deregisterPasswordGenerator(configuration.dn());
350
351 generator.finalizePasswordGenerator();
352 }
353
354
355 return new ConfigChangeResult(resultCode, adminActionRequired);
356 }
357
358 /**
359 * Loads the specified class, instantiates it as a password generator, and
360 * optionally initializes that instance.
361 *
362 * @param className The fully-qualified name of the password generator
363 * class to load, instantiate, and initialize.
364 * @param configuration The configuration to use to initialize the
365 * password generator, or {@code null} if the
366 * password generator should not be initialized.
367 * @param initialize Indicates whether the password generator instance
368 * should be initialized.
369 *
370 * @return The possibly initialized password generator.
371 *
372 * @throws InitializationException If a problem occurred while attempting to
373 * initialize the password generator.
374 */
375 private PasswordGenerator<? extends PasswordGeneratorCfg>
376 loadGenerator(String className,
377 PasswordGeneratorCfg configuration,
378 boolean initialize)
379 throws InitializationException
380 {
381 try
382 {
383 PasswordGeneratorCfgDefn definition =
384 PasswordGeneratorCfgDefn.getInstance();
385 ClassPropertyDefinition propertyDefinition =
386 definition.getJavaClassPropertyDefinition();
387 Class<? extends PasswordGenerator> generatorClass =
388 propertyDefinition.loadClass(className, PasswordGenerator.class);
389 PasswordGenerator<? extends PasswordGeneratorCfg> generator =
390 (PasswordGenerator<? extends PasswordGeneratorCfg>)
391 generatorClass.newInstance();
392
393 if (initialize)
394 {
395 Method method = generator.getClass().getMethod(
396 "initializePasswordGenerator", configuration.configurationClass());
397 method.invoke(generator, configuration);
398 }
399 else
400 {
401 Method method =
402 generator.getClass().getMethod("isConfigurationAcceptable",
403 PasswordGeneratorCfg.class,
404 List.class);
405
406 List<Message> unacceptableReasons = new ArrayList<Message>();
407 Boolean acceptable = (Boolean) method.invoke(generator, 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_PWGENERATOR_CONFIG_NOT_ACCEPTABLE.get(
424 String.valueOf(configuration.dn()), buffer.toString());
425 throw new InitializationException(message);
426 }
427 }
428
429 return generator;
430 }
431 catch (Exception e)
432 {
433 Message message = ERR_CONFIG_PWGENERATOR_INITIALIZATION_FAILED.
434 get(className, String.valueOf(configuration.dn()),
435 stackTraceToSingleLineString(e));
436 throw new InitializationException(message, e);
437 }
438 }
439 }
440