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