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