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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.core; 028 029import static org.opends.messages.ConfigMessages.*; 030import static org.opends.server.util.StaticUtils.*; 031 032import java.util.ArrayList; 033import java.util.List; 034import java.util.concurrent.ConcurrentHashMap; 035 036import org.forgerock.i18n.LocalizableMessage; 037import org.forgerock.i18n.slf4j.LocalizedLogger; 038import org.forgerock.opendj.config.server.ConfigException; 039import org.forgerock.opendj.ldap.ResultCode; 040import org.forgerock.util.Utils; 041import org.opends.server.admin.ClassPropertyDefinition; 042import org.opends.server.admin.server.ConfigurationAddListener; 043import org.opends.server.admin.server.ConfigurationChangeListener; 044import org.opends.server.admin.server.ConfigurationDeleteListener; 045import org.opends.server.admin.server.ServerManagementContext; 046import org.opends.server.admin.std.meta.PasswordGeneratorCfgDefn; 047import org.opends.server.admin.std.server.PasswordGeneratorCfg; 048import org.opends.server.admin.std.server.RootCfg; 049import org.opends.server.api.PasswordGenerator; 050import org.forgerock.opendj.config.server.ConfigChangeResult; 051import org.opends.server.types.DN; 052import org.opends.server.types.InitializationException; 053 054/** 055 * This class defines a utility that will be used to manage the set of password 056 * generators defined in the Directory Server. It will initialize the 057 * generators when the server starts, and then will manage any additions, 058 * removals, or modifications to any password generators while the server is 059 * running. 060 */ 061public class PasswordGeneratorConfigManager 062 implements ConfigurationAddListener<PasswordGeneratorCfg>, 063 ConfigurationDeleteListener<PasswordGeneratorCfg>, 064 ConfigurationChangeListener<PasswordGeneratorCfg> 065{ 066 067 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 068 069 /** 070 * A mapping between the DNs of the config entries and the associated password 071 * generators. 072 */ 073 private final ConcurrentHashMap<DN,PasswordGenerator> passwordGenerators; 074 075 private final ServerContext serverContext; 076 077 /** 078 * Creates a new instance of this password generator config manager. 079 * 080 * @param serverContext 081 * The server context. 082 */ 083 public PasswordGeneratorConfigManager(ServerContext serverContext) 084 { 085 this.serverContext = serverContext; 086 passwordGenerators = new ConcurrentHashMap<>(); 087 } 088 089 /** 090 * Initializes all password generators 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 * generator initialization process to fail. 096 * 097 * @throws InitializationException If a problem occurs while initializing 098 * the password generators that is not 099 * related to the server configuration. 100 */ 101 public void initializePasswordGenerators() 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 // Register as an add and delete listener with the root configuration so we 111 // can be notified if any password generator entries are added or removed. 112 rootConfiguration.addPasswordGeneratorAddListener(this); 113 rootConfiguration.addPasswordGeneratorDeleteListener(this); 114 115 116 //Initialize the existing password generators. 117 for (String generatorName : rootConfiguration.listPasswordGenerators()) 118 { 119 PasswordGeneratorCfg generatorConfiguration = 120 rootConfiguration.getPasswordGenerator(generatorName); 121 generatorConfiguration.addChangeListener(this); 122 123 if (generatorConfiguration.isEnabled()) 124 { 125 String className = generatorConfiguration.getJavaClass(); 126 try 127 { 128 PasswordGenerator<? extends PasswordGeneratorCfg> 129 generator = loadGenerator(className, generatorConfiguration, 130 true); 131 passwordGenerators.put(generatorConfiguration.dn(), generator); 132 DirectoryServer.registerPasswordGenerator(generatorConfiguration.dn(), 133 generator); 134 } 135 catch (InitializationException ie) 136 { 137 logger.error(ie.getMessageObject()); 138 continue; 139 } 140 } 141 } 142 } 143 144 /** {@inheritDoc} */ 145 @Override 146 public boolean isConfigurationChangeAcceptable( 147 PasswordGeneratorCfg configuration, 148 List<LocalizableMessage> 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 /** {@inheritDoc} */ 172 @Override 173 public ConfigChangeResult applyConfigurationChange( 174 PasswordGeneratorCfg configuration) 175 { 176 final ConfigChangeResult ccr = new ConfigChangeResult(); 177 178 179 // Get the existing generator if it's already enabled. 180 PasswordGenerator existingGenerator = 181 passwordGenerators.get(configuration.dn()); 182 183 184 // If the new configuration has the generator disabled, then disable it if 185 // it is enabled, or do nothing if it's already disabled. 186 if (! configuration.isEnabled()) 187 { 188 if (existingGenerator != null) 189 { 190 DirectoryServer.deregisterPasswordGenerator(configuration.dn()); 191 192 PasswordGenerator passwordGenerator = 193 passwordGenerators.remove(configuration.dn()); 194 if (passwordGenerator != null) 195 { 196 passwordGenerator.finalizePasswordGenerator(); 197 } 198 } 199 200 return ccr; 201 } 202 203 204 // Get the class for the password generator. If the generator is already 205 // enabled, then we shouldn't do anything with it although if the class has 206 // changed then we'll at least need to indicate that administrative action 207 // is required. If the generator is disabled, then instantiate the class 208 // and initialize and register it as a password generator. 209 String className = configuration.getJavaClass(); 210 if (existingGenerator != null) 211 { 212 if (! className.equals(existingGenerator.getClass().getName())) 213 { 214 ccr.setAdminActionRequired(true); 215 } 216 217 return ccr; 218 } 219 220 PasswordGenerator<? extends PasswordGeneratorCfg> 221 passwordGenerator = null; 222 try 223 { 224 passwordGenerator = loadGenerator(className, configuration, true); 225 } 226 catch (InitializationException ie) 227 { 228 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 229 ccr.addMessage(ie.getMessageObject()); 230 } 231 232 if (ccr.getResultCode() == ResultCode.SUCCESS) 233 { 234 passwordGenerators.put(configuration.dn(), passwordGenerator); 235 DirectoryServer.registerPasswordGenerator(configuration.dn(), 236 passwordGenerator); 237 } 238 239 return ccr; 240 } 241 /** {@inheritDoc} */ 242 @Override 243 public boolean isConfigurationAddAcceptable( 244 PasswordGeneratorCfg 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 generator. 251 String className = configuration.getJavaClass(); 252 try 253 { 254 loadGenerator(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 /** {@inheritDoc} */ 269 @Override 270 public ConfigChangeResult applyConfigurationAdd( 271 PasswordGeneratorCfg configuration) 272 { 273 final ConfigChangeResult ccr = new ConfigChangeResult(); 274 275 configuration.addChangeListener(this); 276 277 if (! configuration.isEnabled()) 278 { 279 return ccr; 280 } 281 282 PasswordGenerator<? extends PasswordGeneratorCfg> 283 passwordGenerator = null; 284 285 // Get the name of the class and make sure we can instantiate it as a 286 // password generator. 287 String className = configuration.getJavaClass(); 288 try 289 { 290 passwordGenerator = loadGenerator(className, configuration, true); 291 } 292 catch (InitializationException ie) 293 { 294 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 295 ccr.addMessage(ie.getMessageObject()); 296 } 297 298 if (ccr.getResultCode() == ResultCode.SUCCESS) 299 { 300 passwordGenerators.put(configuration.dn(), passwordGenerator); 301 DirectoryServer.registerPasswordGenerator(configuration.dn(), 302 passwordGenerator); 303 } 304 305 return ccr; 306 } 307 308 /** {@inheritDoc} */ 309 @Override 310 public boolean isConfigurationDeleteAcceptable( 311 PasswordGeneratorCfg configuration, List<LocalizableMessage> unacceptableReasons) 312 { 313 // A delete should always be acceptable, so just return true. 314 return true; 315 } 316 317 318 /** {@inheritDoc} */ 319 @Override 320 public ConfigChangeResult applyConfigurationDelete( 321 PasswordGeneratorCfg configuration) 322 { 323 final ConfigChangeResult ccr = new ConfigChangeResult(); 324 325 // See if the entry is registered as a password generator. 326 // If so, deregister it and stop the generator. 327 PasswordGenerator generator = passwordGenerators.remove(configuration.dn()); 328 if (generator != null) 329 { 330 DirectoryServer.deregisterPasswordGenerator(configuration.dn()); 331 332 generator.finalizePasswordGenerator(); 333 } 334 335 return ccr; 336 } 337 338 /** 339 * Loads the specified class, instantiates it as a password generator, and 340 * optionally initializes that instance. 341 * 342 * @param className The fully-qualified name of the password generator 343 * class to load, instantiate, and initialize. 344 * @param configuration The configuration to use to initialize the 345 * password generator, or {@code null} if the 346 * password generator should not be initialized. 347 * @param initialize Indicates whether the password generator instance 348 * should be initialized. 349 * 350 * @return The possibly initialized password generator. 351 * 352 * @throws InitializationException If a problem occurred while attempting to 353 * initialize the password generator. 354 */ 355 private <T extends PasswordGeneratorCfg> PasswordGenerator<T> 356 loadGenerator(String className, 357 T configuration, 358 boolean initialize) 359 throws InitializationException 360 { 361 try 362 { 363 PasswordGeneratorCfgDefn definition = 364 PasswordGeneratorCfgDefn.getInstance(); 365 ClassPropertyDefinition propertyDefinition = 366 definition.getJavaClassPropertyDefinition(); 367 Class<? extends PasswordGenerator> generatorClass = 368 propertyDefinition.loadClass(className, PasswordGenerator.class); 369 PasswordGenerator<T> generator = generatorClass.newInstance(); 370 371 if (initialize) 372 { 373 generator.initializePasswordGenerator(configuration); 374 } 375 else 376 { 377 List<LocalizableMessage> unacceptableReasons = new ArrayList<>(); 378 if (!generator.isConfigurationAcceptable(configuration, unacceptableReasons)) 379 { 380 String reasons = Utils.joinAsString(". ", unacceptableReasons); 381 throw new InitializationException( 382 ERR_CONFIG_PWGENERATOR_CONFIG_NOT_ACCEPTABLE.get(configuration.dn(), reasons)); 383 } 384 } 385 386 return generator; 387 } 388 catch (Exception e) 389 { 390 LocalizableMessage message = ERR_CONFIG_PWGENERATOR_INITIALIZATION_FAILED. 391 get(className, configuration.dn(), stackTraceToSingleLineString(e)); 392 throw new InitializationException(message, e); 393 } 394 } 395} 396