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