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