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