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