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