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