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.opendj.config.server.ConfigException; 038import org.forgerock.util.Utils; 039import org.opends.server.admin.ClassPropertyDefinition; 040import org.opends.server.admin.server.ConfigurationAddListener; 041import org.opends.server.admin.server.ConfigurationChangeListener; 042import org.opends.server.admin.server.ConfigurationDeleteListener; 043import org.opends.server.admin.server.ServerManagementContext; 044import org.opends.server.admin.std.meta.PasswordStorageSchemeCfgDefn; 045import org.opends.server.admin.std.server.PasswordStorageSchemeCfg; 046import org.opends.server.admin.std.server.RootCfg; 047import org.opends.server.api.PasswordStorageScheme; 048import org.forgerock.opendj.config.server.ConfigChangeResult; 049import org.opends.server.types.DN; 050import org.opends.server.types.InitializationException; 051 052/** 053 * This class defines a utility that will be used to manage the set of password 054 * storage schemes defined in the Directory Server. It will initialize the 055 * storage schemes when the server starts, and then will manage any additions, 056 * removals, or modifications to any schemes while the server is running. 057 */ 058public class PasswordStorageSchemeConfigManager 059 implements 060 ConfigurationChangeListener <PasswordStorageSchemeCfg>, 061 ConfigurationAddListener <PasswordStorageSchemeCfg>, 062 ConfigurationDeleteListener <PasswordStorageSchemeCfg> 063{ 064 /** 065 * A mapping between the DNs of the config entries and the associated password 066 * storage schemes. 067 */ 068 private final ConcurrentHashMap<DN,PasswordStorageScheme> storageSchemes; 069 070 private final ServerContext serverContext; 071 072 /** 073 * Creates a new instance of this password storage scheme config manager. 074 * 075 * @param serverContext 076 * The server context. 077 */ 078 public PasswordStorageSchemeConfigManager(ServerContext serverContext) 079 { 080 this.serverContext = serverContext; 081 storageSchemes = new ConcurrentHashMap<>(); 082 } 083 084 085 086 /** 087 * Initializes all password storage schemes currently defined in the Directory 088 * Server configuration. This should only be called at Directory Server 089 * startup. 090 * 091 * @throws ConfigException If a configuration problem causes the password 092 * storage scheme initialization process to fail. 093 * 094 * @throws InitializationException If a problem occurs while initializing 095 * the password storage scheme that is not 096 * related to the server configuration. 097 */ 098 public void initializePasswordStorageSchemes() 099 throws ConfigException, InitializationException 100 { 101 // Get the root configuration object. 102 ServerManagementContext managementContext = 103 ServerManagementContext.getInstance(); 104 RootCfg rootConfiguration = 105 managementContext.getRootConfiguration(); 106 107 // Register as an add and delete listener with the root configuration so we 108 // can be notified if any entry cache entry is added or removed. 109 rootConfiguration.addPasswordStorageSchemeAddListener (this); 110 rootConfiguration.addPasswordStorageSchemeDeleteListener (this); 111 112 // Initialize existing password storage schemes. 113 for (String schemeName: rootConfiguration.listPasswordStorageSchemes()) 114 { 115 // Get the password storage scheme's configuration. 116 PasswordStorageSchemeCfg config = 117 rootConfiguration.getPasswordStorageScheme (schemeName); 118 119 // Register as a change listener for this password storage scheme 120 // entry so that we will be notified of any changes that may be 121 // made to it. 122 config.addChangeListener (this); 123 124 // Ignore this password storage scheme if it is disabled. 125 if (config.isEnabled()) 126 { 127 // Load the password storage scheme implementation class. 128 String className = config.getJavaClass(); 129 loadAndInstallPasswordStorageScheme (className, config); 130 } 131 } 132 } 133 134 135 136 /** {@inheritDoc} */ 137 @Override 138 public boolean isConfigurationChangeAcceptable( 139 PasswordStorageSchemeCfg configuration, 140 List<LocalizableMessage> unacceptableReasons 141 ) 142 { 143 // returned status -- all is fine by default 144 boolean status = true; 145 146 if (configuration.isEnabled()) 147 { 148 // Get the name of the class and make sure we can instantiate it as 149 // a password storage scheme. 150 String className = configuration.getJavaClass(); 151 try 152 { 153 // Load the class but don't initialize it. 154 loadPasswordStorageScheme (className, configuration, false); 155 } 156 catch (InitializationException ie) 157 { 158 unacceptableReasons.add(ie.getMessageObject()); 159 status = false; 160 } 161 } 162 163 return status; 164 } 165 166 167 168 /** {@inheritDoc} */ 169 @Override 170 public ConfigChangeResult applyConfigurationChange( 171 PasswordStorageSchemeCfg configuration 172 ) 173 { 174 final ConfigChangeResult changeResult = new ConfigChangeResult(); 175 176 // Get the configuration entry DN and the associated 177 // password storage scheme class. 178 DN configEntryDN = configuration.dn(); 179 PasswordStorageScheme storageScheme = storageSchemes.get(configEntryDN); 180 181 // If the new configuration has the password storage scheme disabled, 182 // then remove it from the mapping list and clean it. 183 if (! configuration.isEnabled()) 184 { 185 if (storageScheme != null) 186 { 187 uninstallPasswordStorageScheme (configEntryDN); 188 } 189 190 return changeResult; 191 } 192 193 // At this point, new configuration is enabled... 194 // If the current password storage scheme is already enabled then we 195 // don't do anything unless the class has changed in which case we 196 // should indicate that administrative action is required. 197 String newClassName = configuration.getJavaClass(); 198 if (storageScheme != null) 199 { 200 String curClassName = storageScheme.getClass().getName(); 201 boolean classIsNew = !newClassName.equals(curClassName); 202 if (classIsNew) 203 { 204 changeResult.setAdminActionRequired (true); 205 } 206 return changeResult; 207 } 208 209 // New entry cache is enabled and there were no previous one. 210 // Instantiate the new class and initialize it. 211 try 212 { 213 loadAndInstallPasswordStorageScheme (newClassName, configuration); 214 } 215 catch (InitializationException ie) 216 { 217 changeResult.addMessage (ie.getMessageObject()); 218 changeResult.setResultCode (DirectoryServer.getServerErrorResultCode()); 219 return changeResult; 220 } 221 222 return changeResult; 223 } 224 225 226 227 /** {@inheritDoc} */ 228 @Override 229 public boolean isConfigurationAddAcceptable( 230 PasswordStorageSchemeCfg configuration, 231 List<LocalizableMessage> unacceptableReasons 232 ) 233 { 234 // returned status -- all is fine by default 235 boolean status = true; 236 237 // Make sure that no entry already exists with the specified DN. 238 DN configEntryDN = configuration.dn(); 239 if (storageSchemes.containsKey(configEntryDN)) 240 { 241 unacceptableReasons.add (ERR_CONFIG_PWSCHEME_EXISTS.get(configEntryDN)); 242 status = false; 243 } 244 // If configuration is enabled then check that password storage scheme 245 // class can be instantiated. 246 else if (configuration.isEnabled()) 247 { 248 // Get the name of the class and make sure we can instantiate it as 249 // an entry cache. 250 String className = configuration.getJavaClass(); 251 try 252 { 253 // Load the class but don't initialize it. 254 loadPasswordStorageScheme (className, configuration, false); 255 } 256 catch (InitializationException ie) 257 { 258 unacceptableReasons.add (ie.getMessageObject()); 259 status = false; 260 } 261 } 262 263 return status; 264 } 265 266 267 268 /** {@inheritDoc} */ 269 @Override 270 public ConfigChangeResult applyConfigurationAdd( 271 PasswordStorageSchemeCfg configuration 272 ) 273 { 274 final ConfigChangeResult changeResult = new ConfigChangeResult(); 275 276 // Register a change listener with it so we can be notified of changes 277 // to it over time. 278 configuration.addChangeListener(this); 279 280 if (configuration.isEnabled()) 281 { 282 // Instantiate the class as password storage scheme 283 // and initialize it. 284 String className = configuration.getJavaClass(); 285 try 286 { 287 loadAndInstallPasswordStorageScheme (className, configuration); 288 } 289 catch (InitializationException ie) 290 { 291 changeResult.addMessage (ie.getMessageObject()); 292 changeResult.setResultCode (DirectoryServer.getServerErrorResultCode()); 293 return changeResult; 294 } 295 } 296 297 return changeResult; 298 } 299 300 301 302 /** {@inheritDoc} */ 303 @Override 304 public boolean isConfigurationDeleteAcceptable( 305 PasswordStorageSchemeCfg configuration, 306 List<LocalizableMessage> unacceptableReasons 307 ) 308 { 309 // A delete should always be acceptable, so just return true. 310 return true; 311 } 312 313 314 315 /** {@inheritDoc} */ 316 @Override 317 public ConfigChangeResult applyConfigurationDelete( 318 PasswordStorageSchemeCfg configuration 319 ) 320 { 321 final ConfigChangeResult changeResult = new ConfigChangeResult(); 322 323 uninstallPasswordStorageScheme (configuration.dn()); 324 325 return changeResult; 326 } 327 328 329 330 /** 331 * Loads the specified class, instantiates it as a password storage scheme, 332 * and optionally initializes that instance. Any initialized password 333 * storage scheme is registered in the server. 334 * 335 * @param className The fully-qualified name of the password storage 336 * scheme class to load, instantiate, and initialize. 337 * @param configuration The configuration to use to initialize the 338 * password storage scheme, or {@code null} if the 339 * password storage scheme should not be initialized. 340 * 341 * @throws InitializationException If a problem occurred while attempting 342 * to initialize the class. 343 */ 344 private void loadAndInstallPasswordStorageScheme( 345 String className, 346 PasswordStorageSchemeCfg configuration 347 ) 348 throws InitializationException 349 { 350 // Load the password storage scheme class... 351 PasswordStorageScheme 352 <? extends PasswordStorageSchemeCfg> schemeClass; 353 schemeClass = loadPasswordStorageScheme (className, configuration, true); 354 355 // ... and install the password storage scheme in the server. 356 DN configEntryDN = configuration.dn(); 357 storageSchemes.put (configEntryDN, schemeClass); 358 DirectoryServer.registerPasswordStorageScheme (configEntryDN, schemeClass); 359 } 360 361 362 /** 363 * Loads the specified class, instantiates it as a password storage scheme, 364 * and optionally initializes that instance. 365 * 366 * @param className The fully-qualified name of the class 367 * to load, instantiate, and initialize. 368 * @param configuration The configuration to use to initialize the 369 * class. It must not be {@code null}. 370 * @param initialize Indicates whether the password storage scheme 371 * instance should be initialized. 372 * 373 * @return The possibly initialized password storage scheme. 374 * 375 * @throws InitializationException If a problem occurred while attempting 376 * to initialize the class. 377 */ 378 private <T extends PasswordStorageSchemeCfg> PasswordStorageScheme<T> 379 loadPasswordStorageScheme( 380 String className, 381 T configuration, 382 boolean initialize) 383 throws InitializationException 384 { 385 try 386 { 387 ClassPropertyDefinition propertyDefinition; 388 Class<? extends PasswordStorageScheme> schemeClass; 389 390 PasswordStorageSchemeCfgDefn definition = PasswordStorageSchemeCfgDefn.getInstance(); 391 propertyDefinition = definition.getJavaClassPropertyDefinition(); 392 schemeClass = propertyDefinition.loadClass(className, PasswordStorageScheme.class); 393 PasswordStorageScheme<T> passwordStorageScheme = schemeClass.newInstance(); 394 395 if (initialize) 396 { 397 passwordStorageScheme.initializePasswordStorageScheme(configuration); 398 } 399 else 400 { 401 List<LocalizableMessage> unacceptableReasons = new ArrayList<>(); 402 if (!passwordStorageScheme.isConfigurationAcceptable(configuration, unacceptableReasons)) 403 { 404 String reasons = Utils.joinAsString(". ", unacceptableReasons); 405 throw new InitializationException( 406 ERR_CONFIG_PWSCHEME_CONFIG_NOT_ACCEPTABLE.get(configuration.dn(), reasons)); 407 } 408 } 409 410 return passwordStorageScheme; 411 } 412 catch (Exception e) 413 { 414 LocalizableMessage message = ERR_CONFIG_PWSCHEME_INITIALIZATION_FAILED.get(className, 415 configuration.dn(), stackTraceToSingleLineString(e)); 416 throw new InitializationException(message, e); 417 } 418 } 419 420 421 /** 422 * Remove a password storage that has been installed in the server. 423 * 424 * @param configEntryDN the DN of the configuration enry associated to 425 * the password storage scheme to remove 426 */ 427 private void uninstallPasswordStorageScheme( 428 DN configEntryDN 429 ) 430 { 431 PasswordStorageScheme scheme = 432 storageSchemes.remove (configEntryDN); 433 if (scheme != null) 434 { 435 DirectoryServer.deregisterPasswordStorageScheme(configEntryDN); 436 scheme.finalizePasswordStorageScheme(); 437 } 438 } 439} 440