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