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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 import org.opends.messages.Message; 029 030 031 032 import java.lang.reflect.Method; 033 import java.util.ArrayList; 034 import java.util.Iterator; 035 import java.util.List; 036 import java.util.concurrent.ConcurrentHashMap; 037 038 import org.opends.server.admin.ClassPropertyDefinition; 039 import org.opends.server.admin.server.ConfigurationAddListener; 040 import org.opends.server.admin.server.ConfigurationChangeListener; 041 import org.opends.server.admin.server.ConfigurationDeleteListener; 042 import org.opends.server.admin.std.meta.IdentityMapperCfgDefn; 043 import org.opends.server.admin.std.server.IdentityMapperCfg; 044 import org.opends.server.admin.std.server.RootCfg; 045 import org.opends.server.admin.server.ServerManagementContext; 046 import org.opends.server.api.IdentityMapper; 047 import org.opends.server.config.ConfigException; 048 import org.opends.server.types.ConfigChangeResult; 049 import org.opends.server.types.DN; 050 051 052 import org.opends.server.types.InitializationException; 053 import org.opends.server.types.ResultCode; 054 055 import static org.opends.server.loggers.ErrorLogger.*; 056 import static org.opends.messages.ConfigMessages.*; 057 058 import static org.opends.server.util.StaticUtils.*; 059 060 061 062 /** 063 * This class defines a utility that will be used to manage the set of identity 064 * mappers defined in the Directory Server. It will initialize the identity 065 * mappers when the server starts, and then will manage any additions, removals, 066 * or modifications to any identity mappers while the server is running. 067 */ 068 public class IdentityMapperConfigManager 069 implements ConfigurationChangeListener<IdentityMapperCfg>, 070 ConfigurationAddListener<IdentityMapperCfg>, 071 ConfigurationDeleteListener<IdentityMapperCfg> 072 073 { 074 // A mapping between the DNs of the config entries and the associated identity 075 // mappers. 076 private ConcurrentHashMap<DN,IdentityMapper> identityMappers; 077 078 079 080 /** 081 * Creates a new instance of this identity mapper config manager. 082 */ 083 public IdentityMapperConfigManager() 084 { 085 identityMappers = new ConcurrentHashMap<DN,IdentityMapper>(); 086 } 087 088 089 090 /** 091 * Initializes all identity mappers currently defined in the Directory Server 092 * configuration. This should only be called at Directory Server startup. 093 * 094 * @throws ConfigException If a configuration problem causes the identity 095 * mapper initialization process to fail. 096 * 097 * @throws InitializationException If a problem occurs while initializing 098 * the identity mappers that is not related 099 * to the server configuration. 100 */ 101 public void initializeIdentityMappers() 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 111 // Register as an add and delete listener with the root configuration so we 112 // can be notified if any identity mapper entries are added or removed. 113 rootConfiguration.addIdentityMapperAddListener(this); 114 rootConfiguration.addIdentityMapperDeleteListener(this); 115 116 117 //Initialize the existing identity mappers. 118 for (String mapperName : rootConfiguration.listIdentityMappers()) 119 { 120 IdentityMapperCfg mapperConfiguration = 121 rootConfiguration.getIdentityMapper(mapperName); 122 mapperConfiguration.addChangeListener(this); 123 124 if (mapperConfiguration.isEnabled()) 125 { 126 String className = mapperConfiguration.getJavaClass(); 127 try 128 { 129 IdentityMapper mapper = loadMapper(className, mapperConfiguration, 130 true); 131 identityMappers.put(mapperConfiguration.dn(), mapper); 132 DirectoryServer.registerIdentityMapper(mapperConfiguration.dn(), 133 mapper); 134 } 135 catch (InitializationException ie) 136 { 137 logError(ie.getMessageObject()); 138 continue; 139 } 140 } 141 } 142 143 144 // Now that all of the identity mappers are defined, see if the Directory 145 // Server's proxied auth mapper is valid. If not, then log a warning 146 // message. 147 DN mapperDN = DirectoryServer.getProxiedAuthorizationIdentityMapperDN(); 148 if (mapperDN == null) 149 { 150 logError(ERR_CONFIG_IDMAPPER_NO_PROXY_MAPPER_DN.get()); 151 } 152 else if (! identityMappers.containsKey(mapperDN)) 153 { 154 logError(ERR_CONFIG_IDMAPPER_INVALID_PROXY_MAPPER_DN.get( 155 String.valueOf(mapperDN))); 156 } 157 } 158 159 160 161 /** 162 * {@inheritDoc} 163 */ 164 public boolean isConfigurationAddAcceptable( 165 IdentityMapperCfg configuration, 166 List<Message> unacceptableReasons) 167 { 168 if (configuration.isEnabled()) 169 { 170 // Get the name of the class and make sure we can instantiate it as an 171 // identity mapper. 172 String className = configuration.getJavaClass(); 173 try 174 { 175 loadMapper(className, configuration, false); 176 } 177 catch (InitializationException ie) 178 { 179 unacceptableReasons.add(ie.getMessageObject()); 180 return false; 181 } 182 } 183 184 // If we've gotten here, then it's fine. 185 return true; 186 } 187 188 189 190 /** 191 * {@inheritDoc} 192 */ 193 public ConfigChangeResult applyConfigurationAdd( 194 IdentityMapperCfg configuration) 195 { 196 ResultCode resultCode = ResultCode.SUCCESS; 197 boolean adminActionRequired = false; 198 ArrayList<Message> messages = new ArrayList<Message>(); 199 200 configuration.addChangeListener(this); 201 202 if (! configuration.isEnabled()) 203 { 204 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 205 } 206 207 IdentityMapper identityMapper = null; 208 209 // Get the name of the class and make sure we can instantiate it as an 210 // identity mapper. 211 String className = configuration.getJavaClass(); 212 try 213 { 214 identityMapper = loadMapper(className, configuration, true); 215 } 216 catch (InitializationException ie) 217 { 218 if (resultCode == ResultCode.SUCCESS) 219 { 220 resultCode = DirectoryServer.getServerErrorResultCode(); 221 } 222 223 messages.add(ie.getMessageObject()); 224 } 225 226 if (resultCode == ResultCode.SUCCESS) 227 { 228 identityMappers.put(configuration.dn(), identityMapper); 229 DirectoryServer.registerIdentityMapper(configuration.dn(), 230 identityMapper); 231 } 232 233 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 234 } 235 236 237 238 /** 239 * {@inheritDoc} 240 */ 241 public boolean isConfigurationDeleteAcceptable( 242 IdentityMapperCfg configuration, 243 List<Message> unacceptableReasons) 244 { 245 // FIXME -- We should try to perform some check to determine whether the 246 // identity mapper is in use. 247 return true; 248 } 249 250 251 252 /** 253 * {@inheritDoc} 254 */ 255 public ConfigChangeResult applyConfigurationDelete( 256 IdentityMapperCfg configuration) 257 { 258 ResultCode resultCode = ResultCode.SUCCESS; 259 boolean adminActionRequired = false; 260 ArrayList<Message> messages = new ArrayList<Message>(); 261 262 DirectoryServer.deregisterIdentityMapper(configuration.dn()); 263 264 IdentityMapper identityMapper = identityMappers.remove(configuration.dn()); 265 if (identityMapper != null) 266 { 267 identityMapper.finalizeIdentityMapper(); 268 } 269 270 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 271 } 272 273 274 275 /** 276 * {@inheritDoc} 277 */ 278 public boolean isConfigurationChangeAcceptable( 279 IdentityMapperCfg configuration, 280 List<Message> unacceptableReasons) 281 { 282 if (configuration.isEnabled()) 283 { 284 // Get the name of the class and make sure we can instantiate it as an 285 // identity mapper. 286 String className = configuration.getJavaClass(); 287 try 288 { 289 loadMapper(className, configuration, false); 290 } 291 catch (InitializationException ie) 292 { 293 unacceptableReasons.add(ie.getMessageObject()); 294 return false; 295 } 296 } 297 298 // If we've gotten here, then it's fine. 299 return true; 300 } 301 302 303 304 /** 305 * {@inheritDoc} 306 */ 307 public ConfigChangeResult applyConfigurationChange( 308 IdentityMapperCfg configuration) 309 { 310 ResultCode resultCode = ResultCode.SUCCESS; 311 boolean adminActionRequired = false; 312 ArrayList<Message> messages = new ArrayList<Message>(); 313 314 315 // Get the existing mapper if it's already enabled. 316 IdentityMapper existingMapper = identityMappers.get(configuration.dn()); 317 318 319 // If the new configuration has the mapper disabled, then disable it if it 320 // is enabled, or do nothing if it's already disabled. 321 if (! configuration.isEnabled()) 322 { 323 if (existingMapper != null) 324 { 325 DirectoryServer.deregisterIdentityMapper(configuration.dn()); 326 327 IdentityMapper identityMapper = 328 identityMappers.remove(configuration.dn()); 329 if (identityMapper != null) 330 { 331 identityMapper.finalizeIdentityMapper(); 332 } 333 } 334 335 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 336 } 337 338 339 // Get the class for the identity mapper. If the mapper is already enabled, 340 // then we shouldn't do anything with it although if the class has changed 341 // then we'll at least need to indicate that administrative action is 342 // required. If the mapper is disabled, then instantiate the class and 343 // initialize and register it as an identity mapper. 344 String className = configuration.getJavaClass(); 345 if (existingMapper != null) 346 { 347 if (! className.equals(existingMapper.getClass().getName())) 348 { 349 adminActionRequired = true; 350 } 351 352 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 353 } 354 355 IdentityMapper identityMapper = null; 356 try 357 { 358 identityMapper = loadMapper(className, configuration, true); 359 } 360 catch (InitializationException ie) 361 { 362 if (resultCode == ResultCode.SUCCESS) 363 { 364 resultCode = DirectoryServer.getServerErrorResultCode(); 365 } 366 367 messages.add(ie.getMessageObject()); 368 } 369 370 if (resultCode == ResultCode.SUCCESS) 371 { 372 identityMappers.put(configuration.dn(), identityMapper); 373 DirectoryServer.registerIdentityMapper(configuration.dn(), 374 identityMapper); 375 } 376 377 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 378 } 379 380 381 382 /** 383 * Loads the specified class, instantiates it as an identity mapper, and 384 * optionally initializes that instance. 385 * 386 * @param className The fully-qualified name of the identity mapper 387 * class to load, instantiate, and initialize. 388 * @param configuration The configuration to use to initialize the identity 389 * mapper. It must not be {@code null}. 390 * @param initialize Indicates whether the identity mapper instance 391 * should be initialized. 392 * 393 * @return The possibly initialized identity mapper. 394 * 395 * @throws InitializationException If a problem occurred while attempting to 396 * initialize the identity mapper. 397 */ 398 private IdentityMapper loadMapper(String className, 399 IdentityMapperCfg configuration, 400 boolean initialize) 401 throws InitializationException 402 { 403 try 404 { 405 IdentityMapperCfgDefn definition = 406 IdentityMapperCfgDefn.getInstance(); 407 ClassPropertyDefinition propertyDefinition = 408 definition.getJavaClassPropertyDefinition(); 409 Class<? extends IdentityMapper> mapperClass = 410 propertyDefinition.loadClass(className, IdentityMapper.class); 411 IdentityMapper mapper = mapperClass.newInstance(); 412 413 if (initialize) 414 { 415 Method method = mapper.getClass().getMethod("initializeIdentityMapper", 416 configuration.configurationClass()); 417 method.invoke(mapper, configuration); 418 } 419 else 420 { 421 Method method = mapper.getClass().getMethod("isConfigurationAcceptable", 422 IdentityMapperCfg.class, 423 List.class); 424 425 List<Message> unacceptableReasons = new ArrayList<Message>(); 426 Boolean acceptable = (Boolean) method.invoke(mapper, configuration, 427 unacceptableReasons); 428 if (! acceptable) 429 { 430 StringBuilder buffer = new StringBuilder(); 431 if (! unacceptableReasons.isEmpty()) 432 { 433 Iterator<Message> iterator = unacceptableReasons.iterator(); 434 buffer.append(iterator.next()); 435 while (iterator.hasNext()) 436 { 437 buffer.append(". "); 438 buffer.append(iterator.next()); 439 } 440 } 441 442 Message message = ERR_CONFIG_IDMAPPER_CONFIG_NOT_ACCEPTABLE.get( 443 String.valueOf(configuration.dn()), buffer.toString()); 444 throw new InitializationException(message); 445 } 446 } 447 448 return mapper; 449 } 450 catch (Exception e) 451 { 452 Message message = ERR_CONFIG_IDMAPPER_INITIALIZATION_FAILED. 453 get(className, String.valueOf(configuration.dn()), 454 stackTraceToSingleLineString(e)); 455 throw new InitializationException(message, e); 456 } 457 } 458 } 459