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