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