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;
018import static org.opends.messages.ConfigMessages.*;
019
020import java.util.HashSet;
021import java.util.List;
022import java.util.Set;
023import java.util.concurrent.ConcurrentHashMap;
024
025import org.forgerock.i18n.LocalizableMessage;
026import org.forgerock.opendj.config.server.ConfigException;
027import org.forgerock.opendj.ldap.ResultCode;
028import org.opends.server.admin.server.ConfigurationAddListener;
029import org.opends.server.admin.server.ConfigurationChangeListener;
030import org.opends.server.admin.server.ConfigurationDeleteListener;
031import org.opends.server.admin.server.ServerManagementContext;
032import org.opends.server.admin.std.server.RootCfg;
033import org.opends.server.admin.std.server.RootDNCfg;
034import org.opends.server.admin.std.server.RootDNUserCfg;
035import org.forgerock.opendj.config.server.ConfigChangeResult;
036import org.forgerock.opendj.ldap.DN;
037import org.opends.server.types.DirectoryException;
038import org.opends.server.types.InitializationException;
039import org.opends.server.types.Privilege;
040
041/**
042 * This class defines a utility that will be used to manage the set of root
043 * users defined in the Directory Server.  It will handle both the
044 * "cn=Root DNs,cn=config" entry itself (through the root privilege change
045 * listener), and all of its children.
046 */
047public class RootDNConfigManager
048       implements ConfigurationChangeListener<RootDNUserCfg>,
049                  ConfigurationAddListener<RootDNUserCfg>,
050                  ConfigurationDeleteListener<RootDNUserCfg>
051
052{
053  /** A mapping between the actual root DNs and their alternate bind DNs. */
054  private ConcurrentHashMap<DN,HashSet<DN>> alternateBindDNs;
055
056  /**
057   * The root privilege change listener that will handle changes to the
058   * "cn=Root DNs,cn=config" entry itself.
059   */
060  private RootPrivilegeChangeListener rootPrivilegeChangeListener;
061
062  private final ServerContext serverContext;
063
064  /**
065   * Creates a new instance of this root DN config manager.
066   *
067   * @param serverContext
068   *          The server context.
069   */
070  public RootDNConfigManager(ServerContext serverContext)
071  {
072    this.serverContext = serverContext;
073    alternateBindDNs = new ConcurrentHashMap<>();
074    rootPrivilegeChangeListener = new RootPrivilegeChangeListener();
075  }
076
077  /**
078   * Initializes all of the root users currently defined in the Directory Server
079   * configuration, as well as the set of privileges that root users will
080   * inherit by default.
081   *
082   * @throws ConfigException
083   *           If a configuration problem causes the identity mapper
084   *           initialization process to fail.
085   * @throws InitializationException
086   *           If a problem occurs while initializing the identity mappers that
087   *           is not related to the server configuration.
088   */
089  public void initializeRootDNs()
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    // Get the root DN configuration object, use it to set the default root
100    // privileges, and register a change listener for it.
101    RootDNCfg rootDNCfg = rootConfiguration.getRootDN();
102    rootPrivilegeChangeListener.setDefaultRootPrivileges(rootDNCfg);
103    rootDNCfg.addChangeListener(rootPrivilegeChangeListener);
104
105
106    // Register as an add and delete listener for new root DN users.
107    rootDNCfg.addRootDNUserAddListener(this);
108    rootDNCfg.addRootDNUserDeleteListener(this);
109
110
111    // Get the set of root users defined below "cn=Root DNs,cn=config".  For
112    // each one, register as a change listener, and get the set of alternate
113    // bind DNs.
114    for (String name : rootDNCfg.listRootDNUsers())
115    {
116      RootDNUserCfg rootUserCfg = rootDNCfg.getRootDNUser(name);
117      rootUserCfg.addChangeListener(this);
118      DirectoryServer.registerRootDN(rootUserCfg.dn());
119
120      HashSet<DN> altBindDNs = new HashSet<>();
121      for (DN alternateBindDN : rootUserCfg.getAlternateBindDN())
122      {
123        try
124        {
125          altBindDNs.add(alternateBindDN);
126          DirectoryServer.registerAlternateRootDN(rootUserCfg.dn(),
127                                                  alternateBindDN);
128        }
129        catch (DirectoryException de)
130        {
131          throw new InitializationException(de.getMessageObject());
132        }
133      }
134
135      alternateBindDNs.put(rootUserCfg.dn(), altBindDNs);
136    }
137  }
138
139
140
141  /**
142   * Retrieves the set of privileges that will be granted to root users by
143   * default.
144   *
145   * @return  The set of privileges that will be granted to root users by
146   *          default.
147   */
148  public Set<Privilege> getRootPrivileges()
149  {
150    return rootPrivilegeChangeListener.getDefaultRootPrivileges();
151  }
152
153
154
155  /** {@inheritDoc} */
156  @Override
157  public boolean isConfigurationAddAcceptable(RootDNUserCfg configuration,
158                                              List<LocalizableMessage> unacceptableReasons)
159  {
160    // The new root user must not have an alternate bind DN that is already
161    // in use.
162    boolean configAcceptable = true;
163    for (DN altBindDN : configuration.getAlternateBindDN())
164    {
165      DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN);
166      if (existingRootDN != null)
167      {
168        unacceptableReasons.add(ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get(
169            altBindDN, configuration.dn(), existingRootDN));
170        configAcceptable = false;
171      }
172    }
173
174    return configAcceptable;
175  }
176
177
178
179  /** {@inheritDoc} */
180  @Override
181  public ConfigChangeResult applyConfigurationAdd(RootDNUserCfg configuration)
182  {
183    configuration.addChangeListener(this);
184
185    final ConfigChangeResult ccr = new ConfigChangeResult();
186
187    HashSet<DN> altBindDNs = new HashSet<>();
188    for (DN altBindDN : configuration.getAlternateBindDN())
189    {
190      try
191      {
192        DirectoryServer.registerAlternateRootDN(configuration.dn(), altBindDN);
193        altBindDNs.add(altBindDN);
194      }
195      catch (DirectoryException de)
196      {
197        // This shouldn't happen, since the set of DNs should have already been
198        // validated.
199        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
200        ccr.addMessage(de.getMessageObject());
201
202        for (DN dn : altBindDNs)
203        {
204          DirectoryServer.deregisterAlternateRootBindDN(dn);
205        }
206        break;
207      }
208    }
209
210    if (ccr.getResultCode() == ResultCode.SUCCESS)
211    {
212      DirectoryServer.registerRootDN(configuration.dn());
213      alternateBindDNs.put(configuration.dn(), altBindDNs);
214    }
215
216    return ccr;
217  }
218
219
220
221  /** {@inheritDoc} */
222  @Override
223  public boolean isConfigurationDeleteAcceptable(RootDNUserCfg configuration,
224                      List<LocalizableMessage> unacceptableReasons)
225  {
226    return true;
227  }
228
229
230
231  /** {@inheritDoc} */
232  @Override
233  public ConfigChangeResult applyConfigurationDelete(
234                                 RootDNUserCfg configuration)
235  {
236    DirectoryServer.deregisterRootDN(configuration.dn());
237    configuration.removeChangeListener(this);
238
239    final ConfigChangeResult ccr = new ConfigChangeResult();
240
241    HashSet<DN> altBindDNs = alternateBindDNs.remove(configuration.dn());
242    if (altBindDNs != null)
243    {
244      for (DN dn : altBindDNs)
245      {
246        DirectoryServer.deregisterAlternateRootBindDN(dn);
247      }
248    }
249
250    return ccr;
251  }
252
253
254
255  /** {@inheritDoc} */
256  @Override
257  public boolean isConfigurationChangeAcceptable(RootDNUserCfg configuration,
258                      List<LocalizableMessage> unacceptableReasons)
259  {
260    boolean configAcceptable = true;
261
262    // There must not be any new alternate bind DNs that are already in use by
263    // other root users.
264    for (DN altBindDN: configuration.getAlternateBindDN())
265    {
266      DN existingRootDN = DirectoryServer.getActualRootBindDN(altBindDN);
267      if (existingRootDN != null && !existingRootDN.equals(configuration.dn()))
268      {
269        unacceptableReasons.add(ERR_CONFIG_ROOTDN_CONFLICTING_MAPPING.get(
270            altBindDN, configuration.dn(), existingRootDN));
271        configAcceptable = false;
272      }
273    }
274
275    return configAcceptable;
276  }
277
278
279
280  /** {@inheritDoc} */
281  @Override
282  public ConfigChangeResult applyConfigurationChange(
283                                 RootDNUserCfg configuration)
284  {
285    final ConfigChangeResult ccr = new ConfigChangeResult();
286
287    HashSet<DN> setDNs = new HashSet<>();
288    HashSet<DN> addDNs = new HashSet<>();
289    HashSet<DN> delDNs = new HashSet<>(alternateBindDNs.get(configuration.dn()));
290
291    for (DN altBindDN : configuration.getAlternateBindDN())
292    {
293      setDNs.add(altBindDN);
294
295      if (! delDNs.remove(altBindDN))
296      {
297        addDNs.add(altBindDN);
298      }
299    }
300
301    for (DN dn : delDNs)
302    {
303      DirectoryServer.deregisterAlternateRootBindDN(dn);
304    }
305
306    HashSet<DN> addedDNs = new HashSet<>(addDNs.size());
307    for (DN dn : addDNs)
308    {
309      try
310      {
311        DirectoryServer.registerAlternateRootDN(configuration.dn(), dn);
312        addedDNs.add(dn);
313      }
314      catch (DirectoryException de)
315      {
316        // This shouldn't happen, since the set of DNs should have already been
317        // validated.
318        ccr.setResultCode(DirectoryServer.getServerErrorResultCode());
319        ccr.addMessage(de.getMessageObject());
320
321        for (DN addedDN : addedDNs)
322        {
323          DirectoryServer.deregisterAlternateRootBindDN(addedDN);
324        }
325
326        for (DN deletedDN : delDNs)
327        {
328          try
329          {
330            DirectoryServer.registerAlternateRootDN(configuration.dn(),
331                                                    deletedDN);
332          }
333          catch (Exception e)
334          {
335            // This should also never happen.
336            alternateBindDNs.get(configuration.dn()).remove(deletedDN);
337          }
338        }
339      }
340    }
341
342    if (ccr.getResultCode() == ResultCode.SUCCESS)
343    {
344      alternateBindDNs.put(configuration.dn(), setDNs);
345    }
346
347    return ccr;
348  }
349}
350