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