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 2007-2010 Sun Microsystems, Inc.
015 * Portions Copyright 2012-2015 ForgeRock AS.
016 */
017package org.opends.admin.ads;
018
019import static org.forgerock.util.Utils.*;
020import static org.opends.messages.QuickSetupMessages.*;
021
022import java.io.File;
023import java.util.HashMap;
024import java.util.HashSet;
025import java.util.LinkedHashSet;
026import java.util.LinkedList;
027import java.util.Map;
028import java.util.Set;
029import java.util.SortedSet;
030import java.util.TreeSet;
031
032import javax.naming.CompositeName;
033import javax.naming.InvalidNameException;
034import javax.naming.NameAlreadyBoundException;
035import javax.naming.NameNotFoundException;
036import javax.naming.NamingEnumeration;
037import javax.naming.NamingException;
038import javax.naming.NoPermissionException;
039import javax.naming.NotContextException;
040import javax.naming.directory.Attribute;
041import javax.naming.directory.Attributes;
042import javax.naming.directory.BasicAttribute;
043import javax.naming.directory.BasicAttributes;
044import javax.naming.directory.DirContext;
045import javax.naming.directory.SearchControls;
046import javax.naming.directory.SearchResult;
047import javax.naming.ldap.Control;
048import javax.naming.ldap.InitialLdapContext;
049import javax.naming.ldap.LdapContext;
050import javax.naming.ldap.LdapName;
051import javax.naming.ldap.Rdn;
052
053import org.forgerock.i18n.LocalizableMessage;
054import org.forgerock.i18n.slf4j.LocalizedLogger;
055import org.opends.admin.ads.ADSContextException.ErrorType;
056import org.opends.admin.ads.util.ConnectionUtils;
057import org.opends.quicksetup.Constants;
058import org.opends.server.schema.SchemaConstants;
059
060/** Class used to update and read the contents of the Administration Data. */
061public class ADSContext
062{
063  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
064
065  /**
066   * Enumeration containing the different server properties syntaxes that could
067   * be stored in the ADS.
068   */
069  public enum ADSPropertySyntax
070  {
071    /** String syntax. */
072    STRING,
073    /** Integer syntax. */
074    INTEGER,
075    /** Boolean syntax. */
076    BOOLEAN,
077    /** Certificate;binary syntax. */
078    CERTIFICATE_BINARY
079  }
080
081  /** Enumeration containing the different server properties that are stored in the ADS. */
082  public enum ServerProperty
083  {
084    /** The ID used to identify the server. */
085    ID("id",ADSPropertySyntax.STRING),
086    /** The host name of the server. */
087    HOST_NAME("hostname",ADSPropertySyntax.STRING),
088    /** The LDAP port of the server. */
089    LDAP_PORT("ldapport",ADSPropertySyntax.INTEGER),
090    /** The JMX port of the server. */
091    JMX_PORT("jmxport",ADSPropertySyntax.INTEGER),
092    /** The JMX secure port of the server. */
093    JMXS_PORT("jmxsport",ADSPropertySyntax.INTEGER),
094    /** The LDAPS port of the server. */
095    LDAPS_PORT("ldapsport",ADSPropertySyntax.INTEGER),
096    /** The administration connector port of the server. */
097    ADMIN_PORT("adminport",ADSPropertySyntax.INTEGER),
098    /** The certificate used by the server. */
099    CERTIFICATE("certificate",ADSPropertySyntax.STRING),
100    /** The path where the server is installed. */
101    INSTANCE_PATH("instancepath",ADSPropertySyntax.STRING),
102    /** The description of the server. */
103    DESCRIPTION("description",ADSPropertySyntax.STRING),
104    /** The OS of the machine where the server is installed. */
105    HOST_OS("os",ADSPropertySyntax.STRING),
106    /** Whether LDAP is enabled or not. */
107    LDAP_ENABLED("ldapEnabled",ADSPropertySyntax.BOOLEAN),
108    /** Whether LDAPS is enabled or not. */
109    LDAPS_ENABLED("ldapsEnabled",ADSPropertySyntax.BOOLEAN),
110    /** Whether ADMIN is enabled or not. */
111    ADMIN_ENABLED("adminEnabled",ADSPropertySyntax.BOOLEAN),
112    /** Whether StartTLS is enabled or not. */
113    STARTTLS_ENABLED("startTLSEnabled",ADSPropertySyntax.BOOLEAN),
114    /** Whether JMX is enabled or not. */
115    JMX_ENABLED("jmxEnabled",ADSPropertySyntax.BOOLEAN),
116    /** Whether JMX is enabled or not. */
117    JMXS_ENABLED("jmxsEnabled",ADSPropertySyntax.BOOLEAN),
118    /** The location of the server. */
119    LOCATION("location",ADSPropertySyntax.STRING),
120    /** The groups to which this server belongs. */
121    GROUPS("memberofgroups",ADSPropertySyntax.STRING),
122    /** The unique name of the instance key public-key certificate. */
123    INSTANCE_KEY_ID("ds-cfg-key-id",ADSPropertySyntax.STRING),
124    /**
125     * The instance key-pair public-key certificate. Note: This attribute
126     * belongs to an instance key entry, separate from the server entry and
127     * named by the ds-cfg-key-id attribute from the server entry.
128     */
129    INSTANCE_PUBLIC_KEY_CERTIFICATE("ds-cfg-public-key-certificate", ADSPropertySyntax.CERTIFICATE_BINARY);
130
131    private String attrName;
132    private ADSPropertySyntax attSyntax;
133
134    /**
135     * Private constructor.
136     *
137     * @param n
138     *          the name of the attribute.
139     * @param s
140     *          the name of the syntax.
141     */
142    private ServerProperty(String n, ADSPropertySyntax s)
143    {
144      attrName = n;
145      attSyntax = s;
146    }
147
148    /**
149     * Returns the attribute name.
150     *
151     * @return the attribute name.
152     */
153    public String getAttributeName()
154    {
155      return attrName;
156    }
157
158    /**
159     * Returns the attribute syntax.
160     *
161     * @return the attribute syntax.
162     */
163    public ADSPropertySyntax getAttributeSyntax()
164    {
165      return attSyntax;
166    }
167  }
168
169  /** Default global admin UID. */
170  public static final String GLOBAL_ADMIN_UID = "admin";
171
172  private static Map<String, ServerProperty> NAME_TO_SERVER_PROPERTY;
173
174  /**
175   * Get a ServerProperty associated to a name.
176   *
177   * @param name
178   *          The name of the property to retrieve.
179   * @return The corresponding ServerProperty or null if name doesn't match with
180   *         an existing property.
181   */
182  public static ServerProperty getServerPropFromName(String name)
183  {
184    if (NAME_TO_SERVER_PROPERTY == null)
185    {
186      NAME_TO_SERVER_PROPERTY = new HashMap<>();
187      for (ServerProperty s : ServerProperty.values())
188      {
189        NAME_TO_SERVER_PROPERTY.put(s.getAttributeName(), s);
190      }
191    }
192    return NAME_TO_SERVER_PROPERTY.get(name);
193  }
194
195  /** The list of server properties that are multivalued. */
196  private static final Set<ServerProperty> MULTIVALUED_SERVER_PROPERTIES = new HashSet<>();
197  static
198  {
199    MULTIVALUED_SERVER_PROPERTIES.add(ServerProperty.GROUPS);
200  }
201
202  /** The default server group which will contain all registered servers. */
203  public static final String ALL_SERVERGROUP_NAME = "all-servers";
204
205  /** Enumeration containing the different server group properties that are stored in the ADS. */
206  public enum ServerGroupProperty
207  {
208    /** The UID of the server group. */
209    UID("cn"),
210    /** The description of the server group. */
211    DESCRIPTION("description"),
212    /** The members of the server group. */
213    MEMBERS("uniqueMember");
214
215    private String attrName;
216
217    /**
218     * Private constructor.
219     *
220     * @param n
221     *          the attribute name.
222     */
223    private ServerGroupProperty(String n)
224    {
225      attrName = n;
226    }
227
228    /**
229     * Returns the attribute name.
230     *
231     * @return the attribute name.
232     */
233    public String getAttributeName()
234    {
235      return attrName;
236    }
237  }
238
239  /** The list of server group properties that are multivalued. */
240  private static final Set<ServerGroupProperty> MULTIVALUED_SERVER_GROUP_PROPERTIES = new HashSet<>();
241  static
242  {
243    MULTIVALUED_SERVER_GROUP_PROPERTIES.add(ServerGroupProperty.MEMBERS);
244  }
245
246  /** The enumeration containing the different Administrator properties. */
247  public enum AdministratorProperty
248  {
249    /** The UID of the administrator. */
250    UID("id", ADSPropertySyntax.STRING),
251    /** The password of the administrator. */
252    PASSWORD("password", ADSPropertySyntax.STRING),
253    /** The description of the administrator. */
254    DESCRIPTION("description", ADSPropertySyntax.STRING),
255    /** The DN of the administrator. */
256    ADMINISTRATOR_DN("administrator dn", ADSPropertySyntax.STRING),
257    /** The administrator privilege. */
258    PRIVILEGE("privilege", ADSPropertySyntax.STRING);
259
260    private String attrName;
261    private ADSPropertySyntax attrSyntax;
262
263    /**
264     * Private constructor.
265     *
266     * @param n
267     *          the name of the attribute.
268     * @param s
269     *          the name of the syntax.
270     */
271    private AdministratorProperty(String n, ADSPropertySyntax s)
272    {
273      attrName = n;
274      attrSyntax = s;
275    }
276
277    /**
278     * Returns the attribute name.
279     *
280     * @return the attribute name.
281     */
282    public String getAttributeName()
283    {
284      return attrName;
285    }
286
287    /**
288     * Returns the attribute syntax.
289     *
290     * @return the attribute syntax.
291     */
292    public ADSPropertySyntax getAttributeSyntax()
293    {
294      return attrSyntax;
295    }
296  }
297
298  private static HashMap<String, AdministratorProperty> nameToAdminUserProperty;
299
300  /**
301   * Get a AdministratorProperty associated to a name.
302   *
303   * @param name
304   *          The name of the property to retrieve.
305   * @return The corresponding AdministratorProperty or null if name doesn't
306   *         match with an existing property.
307   */
308  public static AdministratorProperty getAdminUserPropFromName(String name)
309  {
310    if (nameToAdminUserProperty == null)
311    {
312      nameToAdminUserProperty = new HashMap<>();
313      for (AdministratorProperty u : AdministratorProperty.values())
314      {
315        nameToAdminUserProperty.put(u.getAttributeName(), u);
316      }
317    }
318    return nameToAdminUserProperty.get(name);
319  }
320
321  /** The context used to retrieve information. */
322  private final InitialLdapContext dirContext;
323
324  /**
325   * Constructor of the ADSContext.
326   *
327   * @param dirContext
328   *          the DirContext that must be used to retrieve information.
329   */
330  public ADSContext(InitialLdapContext dirContext)
331  {
332    this.dirContext = dirContext;
333  }
334
335  /**
336   * Returns the DirContext used to retrieve information by this ADSContext.
337   *
338   * @return the DirContext used to retrieve information by this ADSContext.
339   */
340  public InitialLdapContext getDirContext()
341  {
342    return dirContext;
343  }
344
345  /**
346   * Method called to register a server in the ADS.
347   *
348   * @param serverProperties
349   *          the properties of the server.
350   * @throws ADSContextException
351   *           if the server could not be registered.
352   */
353  public void registerServer(Map<ServerProperty, Object> serverProperties) throws ADSContextException
354  {
355    LdapName dn = makeDNFromServerProperties(serverProperties);
356    BasicAttributes attrs = makeAttrsFromServerProperties(serverProperties, true);
357    try
358    {
359      // This check is required because by default the server container entry
360      // does not exist.
361      if (!isExistingEntry(nameFromDN(getServerContainerDN())))
362      {
363        createContainerEntry(getServerContainerDN());
364      }
365      dirContext.createSubcontext(dn, attrs).close();
366      if (serverProperties.containsKey(ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE))
367      {
368        registerInstanceKeyCertificate(serverProperties, dn);
369      }
370
371      // register this server into "all" groups
372      Map<ServerGroupProperty, Object> serverGroupProperties = new HashMap<>();
373      Set<String> memberList = getServerGroupMemberList(ALL_SERVERGROUP_NAME);
374      if (memberList == null)
375      {
376        memberList = new HashSet<>();
377      }
378      String newMember = "cn=" + Rdn.escapeValue(serverProperties.get(ServerProperty.ID));
379
380      memberList.add(newMember);
381      serverGroupProperties.put(ServerGroupProperty.MEMBERS, memberList);
382
383      updateServerGroup(ALL_SERVERGROUP_NAME, serverGroupProperties);
384
385      // Update the server property "GROUPS"
386      Set<?> rawGroupList = (Set<?>) serverProperties.get(ServerProperty.GROUPS);
387      Set<String> groupList = new HashSet<>();
388      if (rawGroupList != null)
389      {
390        for (Object elm : rawGroupList)
391        {
392          groupList.add(elm.toString());
393        }
394      }
395      groupList.add(ALL_SERVERGROUP_NAME);
396      serverProperties.put(ServerProperty.GROUPS, groupList);
397      updateServer(serverProperties, null);
398    }
399    catch (ADSContextException ace)
400    {
401      throw ace;
402    }
403    catch (NameAlreadyBoundException x)
404    {
405      throw new ADSContextException(ErrorType.ALREADY_REGISTERED);
406    }
407    catch (Exception x)
408    {
409      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
410    }
411  }
412
413  /**
414   * Method called to update the properties of a server in the ADS.
415   *
416   * @param serverProperties
417   *          the new properties of the server.
418   * @param newServerId
419   *          The new server Identifier, or null.
420   * @throws ADSContextException
421   *           if the server could not be registered.
422   */
423  public void updateServer(Map<ServerProperty, Object> serverProperties, String newServerId) throws ADSContextException
424  {
425    LdapName dn = makeDNFromServerProperties(serverProperties);
426
427    try
428    {
429      if (newServerId != null)
430      {
431        Map<ServerProperty, Object> newServerProps = new HashMap<>(serverProperties);
432        newServerProps.put(ServerProperty.ID, newServerId);
433        LdapName newDn = makeDNFromServerProperties(newServerProps);
434        dirContext.rename(dn, newDn);
435        dn = newDn;
436        serverProperties.put(ServerProperty.ID, newServerId);
437      }
438      BasicAttributes attrs = makeAttrsFromServerProperties(serverProperties, false);
439      dirContext.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, attrs);
440      if (serverProperties.containsKey(ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE))
441      {
442        registerInstanceKeyCertificate(serverProperties, dn);
443      }
444    }
445    catch (ADSContextException ace)
446    {
447      throw ace;
448    }
449    catch (NameNotFoundException x)
450    {
451      throw new ADSContextException(ErrorType.NOT_YET_REGISTERED);
452    }
453    catch (Exception x)
454    {
455      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
456    }
457  }
458
459  /**
460   * Method called to unregister a server in the ADS. Note that the server's
461   * instance key-pair public-key certificate entry (created in
462   * <tt>registerServer()</tt>) is left untouched.
463   *
464   * @param serverProperties
465   *          the properties of the server.
466   * @throws ADSContextException
467   *           if the server could not be unregistered.
468   */
469  public void unregisterServer(Map<ServerProperty, Object> serverProperties) throws ADSContextException
470  {
471    LdapName dn = makeDNFromServerProperties(serverProperties);
472    try
473    {
474      // Unregister the server from the server groups.
475      String member = "cn=" + Rdn.escapeValue(serverProperties.get(ServerProperty.ID));
476      Set<Map<ServerGroupProperty, Object>> serverGroups = readServerGroupRegistry();
477      for (Map<ServerGroupProperty, Object> serverGroup : serverGroups)
478      {
479        Set<?> memberList = (Set<?>) serverGroup.get(ServerGroupProperty.MEMBERS);
480        if (memberList != null && memberList.remove(member))
481        {
482          Map<ServerGroupProperty, Object> serverGroupProperties = new HashMap<>();
483          serverGroupProperties.put(ServerGroupProperty.MEMBERS, memberList);
484          String groupName = (String) serverGroup.get(ServerGroupProperty.UID);
485          updateServerGroup(groupName, serverGroupProperties);
486        }
487      }
488
489      dirContext.destroySubcontext(dn);
490    }
491    catch (NameNotFoundException x)
492    {
493      throw new ADSContextException(ErrorType.NOT_YET_REGISTERED);
494    }
495    catch (NamingException x)
496    {
497      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
498    }
499
500    // Unregister the server in server groups
501    NamingEnumeration<SearchResult> ne = null;
502    try
503    {
504      SearchControls sc = new SearchControls();
505
506      String serverID = getServerID(serverProperties);
507      if (serverID != null)
508      {
509        String memberAttrName = ServerGroupProperty.MEMBERS.getAttributeName();
510        String filter = "(" + memberAttrName + "=cn=" + serverID + ")";
511        sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
512        ne = dirContext.search(getServerGroupContainerDN(), filter, sc);
513        while (ne.hasMore())
514        {
515          SearchResult sr = ne.next();
516          String groupDn = sr.getNameInNamespace();
517          BasicAttribute newAttr = new BasicAttribute(memberAttrName);
518          NamingEnumeration<? extends Attribute> attrs = sr.getAttributes().getAll();
519          try
520          {
521            while (attrs.hasMore())
522            {
523              Attribute attr = attrs.next();
524              String attrID = attr.getID();
525
526              if (attrID.equalsIgnoreCase(memberAttrName))
527              {
528                NamingEnumeration<?> ae = attr.getAll();
529                try
530                {
531                  while (ae.hasMore())
532                  {
533                    String value = (String) ae.next();
534                    if (!value.equalsIgnoreCase("cn=" + serverID))
535                    {
536                      newAttr.add(value);
537                    }
538                  }
539                }
540                finally
541                {
542                  handleCloseNamingEnumeration(ae);
543                }
544              }
545            }
546          }
547          finally
548          {
549            handleCloseNamingEnumeration(attrs);
550          }
551          BasicAttributes newAttrs = new BasicAttributes();
552          newAttrs.put(newAttr);
553          if (newAttr.size() > 0)
554          {
555            dirContext.modifyAttributes(groupDn, DirContext.REPLACE_ATTRIBUTE, newAttrs);
556          }
557          else
558          {
559            dirContext.modifyAttributes(groupDn, DirContext.REMOVE_ATTRIBUTE, newAttrs);
560          }
561        }
562      }
563    }
564    catch (NameNotFoundException x)
565    {
566      throw new ADSContextException(ErrorType.BROKEN_INSTALL);
567    }
568    catch (NoPermissionException x)
569    {
570      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
571    }
572    catch (NamingException x)
573    {
574      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
575    }
576    finally
577    {
578      handleCloseNamingEnumeration(ne);
579    }
580  }
581
582  /**
583   * Returns whether a given server is already registered or not.
584   *
585   * @param serverProperties
586   *          the server properties.
587   * @return <CODE>true</CODE> if the server was registered and
588   *         <CODE>false</CODE> otherwise.
589   * @throws ADSContextException
590   *           if something went wrong.
591   */
592  public boolean isServerAlreadyRegistered(Map<ServerProperty, Object> serverProperties) throws ADSContextException
593  {
594    return isExistingEntry(makeDNFromServerProperties(serverProperties));
595  }
596
597  /**
598   * Returns whether a given administrator is already registered or not.
599   *
600   * @param uid
601   *          the administrator UID.
602   * @return <CODE>true</CODE> if the administrator was registered and
603   *         <CODE>false</CODE> otherwise.
604   * @throws ADSContextException
605   *           if something went wrong.
606   */
607  public boolean isAdministratorAlreadyRegistered(String uid) throws ADSContextException
608  {
609    return isExistingEntry(makeDNFromAdministratorProperties(uid));
610  }
611
612  /**
613   * A convenience method that takes some server properties as parameter and if
614   * there is no server registered associated with those properties, registers
615   * it and if it is already registered, updates it.
616   *
617   * @param serverProperties
618   *          the server properties.
619   * @return 0 if the server was registered; 1 if updated (i.e., the server
620   *         entry was already in ADS).
621   * @throws ADSContextException
622   *           if something goes wrong.
623   */
624  public int registerOrUpdateServer(Map<ServerProperty, Object> serverProperties) throws ADSContextException
625  {
626    try
627    {
628      registerServer(serverProperties);
629      return 0;
630    }
631    catch (ADSContextException x)
632    {
633      if (x.getError() == ErrorType.ALREADY_REGISTERED)
634      {
635        updateServer(serverProperties, null);
636        return 1;
637      }
638
639      throw x;
640    }
641  }
642
643  /**
644   * Returns the member list of a group of server.
645   *
646   * @param serverGroupId
647   *          The group name.
648   * @return the member list of a group of server.
649   * @throws ADSContextException
650   *           if something goes wrong.
651   */
652  public Set<String> getServerGroupMemberList(String serverGroupId) throws ADSContextException
653  {
654    LdapName dn = nameFromDN("cn=" + Rdn.escapeValue(serverGroupId) + "," + getServerGroupContainerDN());
655
656    Set<String> result = new HashSet<>();
657    NamingEnumeration<SearchResult> srs = null;
658    NamingEnumeration<? extends Attribute> ne = null;
659    try
660    {
661      SearchControls sc = new SearchControls();
662      sc.setSearchScope(SearchControls.OBJECT_SCOPE);
663      srs = getDirContext().search(dn, "(objectclass=*)", sc);
664
665      if (!srs.hasMore())
666      {
667        return result;
668      }
669      Attributes attrs = srs.next().getAttributes();
670      ne = attrs.getAll();
671      while (ne.hasMore())
672      {
673        Attribute attr = ne.next();
674        String attrID = attr.getID();
675
676        if (!attrID.toLowerCase().equals(ServerGroupProperty.MEMBERS.getAttributeName().toLowerCase()))
677        {
678          continue;
679        }
680
681        // We have the members list
682        NamingEnumeration<?> ae = attr.getAll();
683        try
684        {
685          while (ae.hasMore())
686          {
687            result.add((String) ae.next());
688          }
689        }
690        finally
691        {
692          handleCloseNamingEnumeration(ae);
693        }
694        break;
695      }
696    }
697    catch (NameNotFoundException x)
698    {
699      result = new HashSet<>();
700    }
701    catch (NoPermissionException x)
702    {
703      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
704    }
705    catch (NamingException x)
706    {
707      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
708    }
709    finally
710    {
711      handleCloseNamingEnumeration(srs);
712      handleCloseNamingEnumeration(ne);
713    }
714    return result;
715  }
716
717  /**
718   * Returns a set containing the servers that are registered in the ADS.
719   *
720   * @return a set containing the servers that are registered in the ADS.
721   * @throws ADSContextException
722   *           if something goes wrong.
723   */
724  public Set<Map<ServerProperty, Object>> readServerRegistry() throws ADSContextException
725  {
726    Set<Map<ServerProperty, Object>> result = new HashSet<>();
727    NamingEnumeration<SearchResult> ne = null;
728    try
729    {
730      SearchControls sc = new SearchControls();
731
732      sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
733      ne = dirContext.search(getServerContainerDN(), "(objectclass=*)", sc);
734      while (ne.hasMore())
735      {
736        SearchResult sr = ne.next();
737        Map<ServerProperty, Object> properties = makePropertiesFromServerAttrs(sr.getAttributes());
738        Object keyId = properties.get(ServerProperty.INSTANCE_KEY_ID);
739        if (keyId != null)
740        {
741          NamingEnumeration<SearchResult> ne2 = null;
742          try
743          {
744            SearchControls sc1 = new SearchControls();
745            sc1.setSearchScope(SearchControls.ONELEVEL_SCOPE);
746            final String attrIDs[] = { "ds-cfg-public-key-certificate;binary" };
747            sc1.setReturningAttributes(attrIDs);
748
749            ne2 = dirContext.search(getInstanceKeysContainerDN(), "(ds-cfg-key-id=" + keyId + ")", sc);
750            boolean found = false;
751            while (ne2.hasMore())
752            {
753              SearchResult certEntry = ne2.next();
754              Attribute certAttr = certEntry.getAttributes().get(attrIDs[0]);
755              properties.put(ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE, certAttr.get());
756              found = true;
757            }
758            if (!found)
759            {
760              logger.warn(LocalizableMessage.raw("Could not find public key for " + properties));
761            }
762          }
763          catch (NameNotFoundException x)
764          {
765            logger.warn(LocalizableMessage.raw("Could not find public key for " + properties));
766          }
767          finally
768          {
769            handleCloseNamingEnumeration(ne2);
770          }
771        }
772        result.add(properties);
773      }
774    }
775    catch (NameNotFoundException x)
776    {
777      throw new ADSContextException(ErrorType.BROKEN_INSTALL);
778    }
779    catch (NoPermissionException x)
780    {
781      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
782    }
783    catch (NamingException x)
784    {
785      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
786    }
787    finally
788    {
789      handleCloseNamingEnumeration(ne);
790    }
791
792    return result;
793  }
794
795  /**
796   * Creates a Server Group in the ADS.
797   *
798   * @param serverGroupProperties
799   *          the properties of the server group to be created.
800   * @throws ADSContextException
801   *           if something goes wrong.
802   */
803  public void createServerGroup(Map<ServerGroupProperty, Object> serverGroupProperties) throws ADSContextException
804  {
805    LdapName dn = makeDNFromServerGroupProperties(serverGroupProperties);
806    BasicAttributes attrs = makeAttrsFromServerGroupProperties(serverGroupProperties);
807    // Add the objectclass attribute value
808    Attribute oc = new BasicAttribute("objectclass");
809    oc.add("top");
810    oc.add("groupOfUniqueNames");
811    attrs.put(oc);
812    try
813    {
814      DirContext ctx = dirContext.createSubcontext(dn, attrs);
815      ctx.close();
816    }
817    catch (NameAlreadyBoundException x)
818    {
819      throw new ADSContextException(ErrorType.ALREADY_REGISTERED);
820    }
821    catch (NamingException x)
822    {
823      throw new ADSContextException(ErrorType.BROKEN_INSTALL, x);
824    }
825  }
826
827  /**
828   * Updates the properties of a Server Group in the ADS.
829   *
830   * @param serverGroupProperties
831   *          the new properties of the server group to be updated.
832   * @param groupID
833   *          The group name.
834   * @throws ADSContextException
835   *           if something goes wrong.
836   */
837  public void updateServerGroup(String groupID, Map<ServerGroupProperty, Object> serverGroupProperties)
838      throws ADSContextException
839  {
840    LdapName dn = nameFromDN("cn=" + Rdn.escapeValue(groupID) + "," + getServerGroupContainerDN());
841    try
842    {
843      // Entry renaming ?
844      if (serverGroupProperties.containsKey(ServerGroupProperty.UID))
845      {
846        String newGroupId = serverGroupProperties.get(ServerGroupProperty.UID).toString();
847        if (!newGroupId.equals(groupID))
848        {
849          // Rename to entry
850          LdapName newDN = nameFromDN("cn=" + Rdn.escapeValue(newGroupId) + "," + getServerGroupContainerDN());
851          dirContext.rename(dn, newDN);
852          dn = newDN;
853        }
854
855        // In any case, we remove the "cn" attribute.
856        serverGroupProperties.remove(ServerGroupProperty.UID);
857      }
858      if (serverGroupProperties.isEmpty())
859      {
860        return;
861      }
862
863      BasicAttributes attrs = makeAttrsFromServerGroupProperties(serverGroupProperties);
864      // attribute modification
865      dirContext.modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, attrs);
866    }
867    catch (NameNotFoundException x)
868    {
869      throw new ADSContextException(ErrorType.NOT_YET_REGISTERED);
870    }
871    catch (NameAlreadyBoundException x)
872    {
873      throw new ADSContextException(ErrorType.ALREADY_REGISTERED);
874    }
875    catch (NamingException x)
876    {
877      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
878    }
879  }
880
881  /**
882   * Updates the properties of a Server Group in the ADS.
883   *
884   * @param serverGroupProperties
885   *          the new properties of the server group to be updated.
886   * @param groupID
887   *          The group name.
888   * @throws ADSContextException
889   *           if something goes wrong.
890   */
891  public void removeServerGroupProp(String groupID, Set<ServerGroupProperty> serverGroupProperties)
892      throws ADSContextException
893  {
894    LdapName dn = nameFromDN("cn=" + Rdn.escapeValue(groupID) + "," + getServerGroupContainerDN());
895    BasicAttributes attrs = makeAttrsFromServerGroupProperties(serverGroupProperties);
896    try
897    {
898      dirContext.modifyAttributes(dn, DirContext.REMOVE_ATTRIBUTE, attrs);
899    }
900    catch (NameAlreadyBoundException x)
901    {
902      throw new ADSContextException(ErrorType.ALREADY_REGISTERED);
903    }
904    catch (NamingException x)
905    {
906      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
907    }
908  }
909
910  /**
911   * Deletes a Server Group in the ADS.
912   *
913   * @param serverGroupProperties
914   *          the properties of the server group to be deleted.
915   * @throws ADSContextException
916   *           if something goes wrong.
917   */
918  public void deleteServerGroup(Map<ServerGroupProperty, Object> serverGroupProperties) throws ADSContextException
919  {
920    LdapName dn = makeDNFromServerGroupProperties(serverGroupProperties);
921    try
922    {
923      dirContext.destroySubcontext(dn);
924    }
925    catch (NamingException x)
926    {
927      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
928    }
929  }
930
931  /**
932   * Returns a set containing the server groups that are defined in the ADS.
933   *
934   * @return a set containing the server groups that are defined in the ADS.
935   * @throws ADSContextException
936   *           if something goes wrong.
937   */
938  public Set<Map<ServerGroupProperty, Object>> readServerGroupRegistry() throws ADSContextException
939  {
940    Set<Map<ServerGroupProperty, Object>> result = new HashSet<>();
941    NamingEnumeration<SearchResult> ne = null;
942    try
943    {
944      SearchControls sc = new SearchControls();
945
946      sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
947      ne = dirContext.search(getServerGroupContainerDN(), "(objectclass=*)", sc);
948      while (ne.hasMore())
949      {
950        SearchResult sr = ne.next();
951        Map<ServerGroupProperty, Object> properties = makePropertiesFromServerGroupAttrs(sr.getAttributes());
952        result.add(properties);
953      }
954    }
955    catch (NameNotFoundException x)
956    {
957      throw new ADSContextException(ErrorType.BROKEN_INSTALL);
958    }
959    catch (NoPermissionException x)
960    {
961      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
962    }
963    catch (NamingException x)
964    {
965      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
966    }
967    finally
968    {
969      handleCloseNamingEnumeration(ne);
970    }
971    return result;
972  }
973
974  /**
975   * Returns a set containing the administrators that are defined in the ADS.
976   *
977   * @return a set containing the administrators that are defined in the ADS.
978   * @throws ADSContextException
979   *           if something goes wrong.
980   */
981  public Set<Map<AdministratorProperty, Object>> readAdministratorRegistry() throws ADSContextException
982  {
983    Set<Map<AdministratorProperty, Object>> result = new HashSet<>();
984    NamingEnumeration<SearchResult> ne = null;
985    try
986    {
987      SearchControls sc = new SearchControls();
988
989      sc.setSearchScope(SearchControls.ONELEVEL_SCOPE);
990      String[] attList = { "cn", "userpassword", "ds-privilege-name", "description" };
991      sc.setReturningAttributes(attList);
992      ne = dirContext.search(getAdministratorContainerDN(), "(objectclass=*)", sc);
993      while (ne.hasMore())
994      {
995        SearchResult sr = ne.next();
996        Map<AdministratorProperty, Object> properties =
997            makePropertiesFromAdministratorAttrs(getRdn(sr.getName()), sr.getAttributes());
998        result.add(properties);
999      }
1000    }
1001    catch (NameNotFoundException x)
1002    {
1003      throw new ADSContextException(ErrorType.BROKEN_INSTALL);
1004    }
1005    catch (NoPermissionException x)
1006    {
1007      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
1008    }
1009    catch (NamingException x)
1010    {
1011      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
1012    }
1013    finally
1014    {
1015      handleCloseNamingEnumeration(ne);
1016    }
1017
1018    return result;
1019  }
1020
1021  /**
1022   * Creates the Administration Data in the server. The call to this method
1023   * assumes that OpenDJ.jar has already been loaded.
1024   *
1025   * @param backendName
1026   *          the backend name which will handle admin information.
1027   *          <CODE>null</CODE> to use the default backend name for the admin
1028   *          information.
1029   * @throws ADSContextException
1030   *           if something goes wrong.
1031   */
1032  public void createAdminData(String backendName) throws ADSContextException
1033  {
1034    // Add the administration suffix
1035    createAdministrationSuffix(backendName);
1036    createAdminDataContainers();
1037  }
1038
1039  /** Create container entries. */
1040  private void createAdminDataContainers() throws ADSContextException
1041  {
1042    // Create the DIT below the administration suffix
1043    if (!isExistingEntry(nameFromDN(getAdministrationSuffixDN())))
1044    {
1045      createTopContainerEntry();
1046    }
1047    if (!isExistingEntry(nameFromDN(getAdministratorContainerDN())))
1048    {
1049      createAdministratorContainerEntry();
1050    }
1051    if (!isExistingEntry(nameFromDN(getServerContainerDN())))
1052    {
1053      createContainerEntry(getServerContainerDN());
1054    }
1055    if (!isExistingEntry(nameFromDN(getServerGroupContainerDN())))
1056    {
1057      createContainerEntry(getServerGroupContainerDN());
1058    }
1059
1060    // Add the default "all-servers" group
1061    if (!isExistingEntry(nameFromDN(getAllServerGroupDN())))
1062    {
1063      Map<ServerGroupProperty, Object> allServersGroupsMap = new HashMap<>();
1064      allServersGroupsMap.put(ServerGroupProperty.UID, ALL_SERVERGROUP_NAME);
1065      createServerGroup(allServersGroupsMap);
1066    }
1067
1068    // Create the CryptoManager instance key DIT below the administration suffix
1069    if (!isExistingEntry(nameFromDN(getInstanceKeysContainerDN())))
1070    {
1071      createContainerEntry(getInstanceKeysContainerDN());
1072    }
1073
1074    // Create the CryptoManager secret key DIT below the administration suffix
1075    if (!isExistingEntry(nameFromDN(getSecretKeysContainerDN())))
1076    {
1077      createContainerEntry(getSecretKeysContainerDN());
1078    }
1079  }
1080
1081  /**
1082   * Removes the administration data.
1083   *
1084   * @param removeAdministrators
1085   *          {@code true} if administrators should be removed. It may not be
1086   *          possible to remove administrators if the operation is being
1087   *          performed by one of the administrators because it will cause the
1088   *          administrator to be disconnected.
1089   * @throws ADSContextException
1090   *           if something goes wrong.
1091   */
1092  public void removeAdminData(boolean removeAdministrators) throws ADSContextException
1093  {
1094    String[] dns = { getServerContainerDN(), getServerGroupContainerDN(),
1095      removeAdministrators ? getAdministratorContainerDN() : null };
1096    try
1097    {
1098      Control[] controls = new Control[] { new SubtreeDeleteControl() };
1099      LdapContext tmpContext = dirContext.newInstance(controls);
1100      try
1101      {
1102        for (String dn : dns)
1103        {
1104          if (dn != null)
1105          {
1106            LdapName ldapName = nameFromDN(dn);
1107            if (isExistingEntry(ldapName))
1108            {
1109              tmpContext.destroySubcontext(dn);
1110            }
1111          }
1112        }
1113      }
1114      finally
1115      {
1116        try
1117        {
1118          tmpContext.close();
1119        }
1120        catch (Exception ex)
1121        {
1122          logger.warn(LocalizableMessage.raw("Error while closing LDAP connection after removing admin data", ex));
1123        }
1124      }
1125      // Recreate the container entries:
1126      createAdminDataContainers();
1127    }
1128    catch (NamingException x)
1129    {
1130      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
1131    }
1132  }
1133
1134  /**
1135   * Returns <CODE>true</CODE> if the server contains Administration Data and
1136   * <CODE>false</CODE> otherwise.
1137   *
1138   * @return <CODE>true</CODE> if the server contains Administration Data and
1139   *         <CODE>false</CODE> otherwise.
1140   * @throws ADSContextException
1141   *           if something goes wrong.
1142   */
1143  public boolean hasAdminData() throws ADSContextException
1144  {
1145    String[] dns = { getAdministratorContainerDN(), getAllServerGroupDN(), getServerContainerDN(),
1146      getInstanceKeysContainerDN(), getSecretKeysContainerDN() };
1147    boolean hasAdminData = true;
1148    for (int i = 0; i < dns.length && hasAdminData; i++)
1149    {
1150      hasAdminData = isExistingEntry(nameFromDN(dns[i]));
1151    }
1152    return hasAdminData;
1153  }
1154
1155  /**
1156   * Returns the DN of the administrator for a given UID.
1157   *
1158   * @param uid
1159   *          the UID to be used to generate the DN.
1160   * @return the DN of the administrator for the given UID:
1161   */
1162  public static String getAdministratorDN(String uid)
1163  {
1164    return "cn=" + Rdn.escapeValue(uid) + "," + getAdministratorContainerDN();
1165  }
1166
1167  /**
1168   * Creates an Administrator in the ADS.
1169   *
1170   * @param adminProperties
1171   *          the properties of the administrator to be created.
1172   * @throws ADSContextException
1173   *           if something goes wrong.
1174   */
1175  public void createAdministrator(Map<AdministratorProperty, Object> adminProperties) throws ADSContextException
1176  {
1177    LdapName dnCentralAdmin = makeDNFromAdministratorProperties(adminProperties);
1178    BasicAttributes attrs = makeAttrsFromAdministratorProperties(adminProperties, true, null);
1179
1180    try
1181    {
1182      DirContext ctx = dirContext.createSubcontext(dnCentralAdmin, attrs);
1183      ctx.close();
1184    }
1185    catch (NameAlreadyBoundException x)
1186    {
1187      throw new ADSContextException(ErrorType.ALREADY_REGISTERED);
1188    }
1189    catch (NoPermissionException x)
1190    {
1191      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
1192    }
1193    catch (NamingException x)
1194    {
1195      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
1196    }
1197  }
1198
1199  /**
1200   * Deletes the administrator in the ADS.
1201   *
1202   * @param adminProperties
1203   *          the properties of the administrator to be deleted.
1204   * @throws ADSContextException
1205   *           if something goes wrong.
1206   */
1207  public void deleteAdministrator(Map<AdministratorProperty, Object> adminProperties) throws ADSContextException
1208  {
1209    LdapName dnCentralAdmin = makeDNFromAdministratorProperties(adminProperties);
1210
1211    try
1212    {
1213      dirContext.destroySubcontext(dnCentralAdmin);
1214    }
1215    catch (NameNotFoundException | NotContextException x)
1216    {
1217      throw new ADSContextException(ErrorType.NOT_YET_REGISTERED);
1218    }
1219    catch (NoPermissionException x)
1220    {
1221      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
1222    }
1223    catch (NamingException x)
1224    {
1225      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
1226    }
1227  }
1228
1229  /**
1230   * Updates and administrator registered in the ADS.
1231   *
1232   * @param adminProperties
1233   *          the new properties of the administrator.
1234   * @param newAdminUserId
1235   *          The new admin user Identifier, or null.
1236   * @throws ADSContextException
1237   *           if something goes wrong.
1238   */
1239  public void updateAdministrator(Map<AdministratorProperty, Object> adminProperties, String newAdminUserId)
1240      throws ADSContextException
1241  {
1242    LdapName dnCentralAdmin = makeDNFromAdministratorProperties(adminProperties);
1243
1244    boolean updatePassword = adminProperties.containsKey(AdministratorProperty.PASSWORD);
1245
1246    NamingEnumeration<?> currentPrivileges = null;
1247    try
1248    {
1249      // Entry renaming
1250      if (newAdminUserId != null)
1251      {
1252        Map<AdministratorProperty, Object> newAdminUserProps = new HashMap<>(adminProperties);
1253        newAdminUserProps.put(AdministratorProperty.UID, newAdminUserId);
1254        LdapName newDn = makeDNFromAdministratorProperties(newAdminUserProps);
1255        dirContext.rename(dnCentralAdmin, newDn);
1256        dnCentralAdmin = newDn;
1257        adminProperties.put(AdministratorProperty.UID, newAdminUserId);
1258      }
1259
1260      // if modification includes 'privilege', we have to get first the
1261      // current privileges list.
1262      if (adminProperties.containsKey(AdministratorProperty.PRIVILEGE))
1263      {
1264        SearchControls sc = new SearchControls();
1265        sc.setSearchScope(SearchControls.OBJECT_SCOPE);
1266        String[] attList = { "ds-privilege-name" };
1267        sc.setReturningAttributes(attList);
1268        NamingEnumeration<SearchResult> ne = dirContext.search(dnCentralAdmin, "(objectclass=*)", sc);
1269        try
1270        {
1271          while (ne.hasMore())
1272          {
1273            currentPrivileges = ne.next().getAttributes().get("ds-privilege-name").getAll();
1274          }
1275        }
1276        finally
1277        {
1278          handleCloseNamingEnumeration(ne);
1279        }
1280      }
1281
1282      // Replace properties, if needed.
1283      if (adminProperties.size() > 1)
1284      {
1285        BasicAttributes attrs =
1286            makeAttrsFromAdministratorProperties(adminProperties, updatePassword, currentPrivileges);
1287        dirContext.modifyAttributes(dnCentralAdmin, DirContext.REPLACE_ATTRIBUTE, attrs);
1288      }
1289    }
1290    catch (NameNotFoundException x)
1291    {
1292      throw new ADSContextException(ErrorType.NOT_YET_REGISTERED);
1293    }
1294    catch (NoPermissionException x)
1295    {
1296      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
1297    }
1298    catch (NamingException x)
1299    {
1300      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
1301    }
1302    finally
1303    {
1304      handleCloseNamingEnumeration(currentPrivileges);
1305    }
1306  }
1307
1308  /**
1309   * Returns the DN of the suffix that contains the administration data.
1310   *
1311   * @return the DN of the suffix that contains the administration data.
1312   */
1313  public static String getAdministrationSuffixDN()
1314  {
1315    return "cn=admin data";
1316  }
1317
1318  /**
1319   * This method returns the DN of the entry that corresponds to the given host
1320   * name and installation path.
1321   *
1322   * @param hostname
1323   *          the host name.
1324   * @param ipath
1325   *          the installation path.
1326   * @return the DN of the entry that corresponds to the given host name and
1327   *         installation path.
1328   * @throws ADSContextException
1329   *           if something goes wrong.
1330   */
1331  private static LdapName makeDNFromHostnameAndPath(String hostname, String ipath) throws ADSContextException
1332  {
1333    return nameFromDN("cn=" + Rdn.escapeValue(hostname + "@" + ipath) + "," + getServerContainerDN());
1334  }
1335
1336  /**
1337   * This method returns the DN of the entry that corresponds to the given host
1338   * name port representation.
1339   *
1340   * @param serverUniqueId
1341   *          the host name and port.
1342   * @return the DN of the entry that corresponds to the given host name and
1343   *         port.
1344   * @throws ADSContextException
1345   *           if something goes wrong.
1346   */
1347  private static LdapName makeDNFromServerUniqueId(String serverUniqueId) throws ADSContextException
1348  {
1349    return nameFromDN("cn=" + Rdn.escapeValue(serverUniqueId) + "," + getServerContainerDN());
1350  }
1351
1352  /**
1353   * This method returns the DN of the entry that corresponds to the given
1354   * server group properties.
1355   *
1356   * @param serverGroupProperties
1357   *          the server group properties
1358   * @return the DN of the entry that corresponds to the given server group
1359   *         properties.
1360   * @throws ADSContextException
1361   *           if something goes wrong.
1362   */
1363  private static LdapName makeDNFromServerGroupProperties(Map<ServerGroupProperty, Object> serverGroupProperties)
1364      throws ADSContextException
1365  {
1366    String serverGroupId = (String) serverGroupProperties.get(ServerGroupProperty.UID);
1367    if (serverGroupId == null)
1368    {
1369      throw new ADSContextException(ErrorType.MISSING_NAME);
1370    }
1371    return nameFromDN("cn=" + Rdn.escapeValue(serverGroupId) + "," + getServerGroupContainerDN());
1372  }
1373
1374  /**
1375   * This method returns the DN of the entry that corresponds to the given
1376   * server properties.
1377   *
1378   * @param serverProperties
1379   *          the server properties.
1380   * @return the DN of the entry that corresponds to the given server
1381   *         properties.
1382   * @throws ADSContextException
1383   *           if something goes wrong.
1384   */
1385  private static LdapName makeDNFromServerProperties(Map<ServerProperty, Object> serverProperties)
1386      throws ADSContextException
1387  {
1388    String serverID = getServerID(serverProperties);
1389    if (serverID != null)
1390    {
1391      return makeDNFromServerUniqueId(serverID);
1392    }
1393
1394    String hostname = getHostname(serverProperties);
1395    try
1396    {
1397      String ipath = getInstallPath(serverProperties);
1398      return makeDNFromHostnameAndPath(hostname, ipath);
1399    }
1400    catch (ADSContextException ace)
1401    {
1402      ServerDescriptor s = ServerDescriptor.createStandalone(serverProperties);
1403      return makeDNFromServerUniqueId(s.getHostPort(true));
1404    }
1405  }
1406
1407  /**
1408   * This method returns the DN of the entry that corresponds to the given
1409   * server properties.
1410   *
1411   * @param serverProperties
1412   *          the server properties.
1413   * @return the DN of the entry that corresponds to the given server
1414   *         properties.
1415   * @throws ADSContextException
1416   *           if something goes wrong.
1417   */
1418  public static String getServerIdFromServerProperties(Map<ServerProperty, Object> serverProperties)
1419      throws ADSContextException
1420  {
1421    LdapName ldapName = makeDNFromServerProperties(serverProperties);
1422    String rdn = ldapName.get(ldapName.size() - 1);
1423    int pos = rdn.indexOf("=");
1424    return rdn.substring(pos + 1);
1425  }
1426
1427  /**
1428   * This method returns the DN of the entry that corresponds to the given
1429   * administrator properties.
1430   *
1431   * @param adminProperties
1432   *          the administrator properties.
1433   * @return the DN of the entry that corresponds to the given administrator
1434   *         properties.
1435   * @throws ADSContextException
1436   *           if something goes wrong.
1437   */
1438  private static LdapName makeDNFromAdministratorProperties(Map<AdministratorProperty, Object> adminProperties)
1439      throws ADSContextException
1440  {
1441    return makeDNFromAdministratorProperties(getAdministratorUID(adminProperties));
1442  }
1443
1444  /**
1445   * This method returns the DN of the entry that corresponds to the given
1446   * administrator properties.
1447   *
1448   * @param adminUid
1449   *          the administrator uid.
1450   * @return the DN of the entry that corresponds to the given administrator
1451   *         properties.
1452   * @throws ADSContextException
1453   *           if something goes wrong.
1454   */
1455  private static LdapName makeDNFromAdministratorProperties(String adminUid) throws ADSContextException
1456  {
1457    return nameFromDN(getAdministratorDN(adminUid));
1458  }
1459
1460  /**
1461   * Returns the attributes for some administrator properties.
1462   *
1463   * @param adminProperties
1464   *          the administrator properties.
1465   * @param passwordRequired
1466   *          Indicates if the properties should include the password.
1467   * @param currentPrivileges
1468   *          The current privilege list or null.
1469   * @return the attributes for the given administrator properties.
1470   * @throws ADSContextException
1471   *           if something goes wrong.
1472   */
1473  private static BasicAttributes makeAttrsFromAdministratorProperties(
1474      Map<AdministratorProperty, Object> adminProperties, boolean passwordRequired,
1475      NamingEnumeration<?> currentPrivileges) throws ADSContextException
1476  {
1477    BasicAttributes attrs = new BasicAttributes();
1478    Attribute oc = new BasicAttribute("objectclass");
1479    if (passwordRequired)
1480    {
1481      attrs.put("userPassword", getAdministratorPassword(adminProperties));
1482    }
1483    oc.add("top");
1484    oc.add("person");
1485    attrs.put(oc);
1486    attrs.put("sn", GLOBAL_ADMIN_UID);
1487    if (adminProperties.containsKey(AdministratorProperty.DESCRIPTION))
1488    {
1489      attrs.put("description", adminProperties.get(AdministratorProperty.DESCRIPTION));
1490    }
1491    Attribute privilegeAtt;
1492    if (adminProperties.containsKey(AdministratorProperty.PRIVILEGE))
1493    {
1494      // We assume that privilege strings provided in
1495      // AdministratorProperty.PRIVILEGE
1496      // are valid privileges represented as a LinkedList of string.
1497      privilegeAtt = new BasicAttribute("ds-privilege-name");
1498      if (currentPrivileges != null)
1499      {
1500        while (currentPrivileges.hasMoreElements())
1501        {
1502          privilegeAtt.add(currentPrivileges.nextElement().toString());
1503        }
1504      }
1505
1506      LinkedList<?> privileges = (LinkedList<?>) adminProperties.get(AdministratorProperty.PRIVILEGE);
1507      for (Object o : privileges)
1508      {
1509        String p = o.toString();
1510        if (p.startsWith("-"))
1511        {
1512          privilegeAtt.remove(p.substring(1));
1513        }
1514        else
1515        {
1516          privilegeAtt.add(p);
1517        }
1518      }
1519    }
1520    else
1521    {
1522      privilegeAtt = addRootPrivileges();
1523    }
1524    attrs.put(privilegeAtt);
1525
1526    // Add the RootDNs Password policy so the password do not expire.
1527    attrs.put("ds-pwp-password-policy-dn", "cn=Root Password Policy,cn=Password Policies,cn=config");
1528
1529    return attrs;
1530  }
1531
1532  /**
1533   * Builds an attribute which contains 'root' privileges.
1534   *
1535   * @return The attribute which contains 'root' privileges.
1536   */
1537  private static Attribute addRootPrivileges()
1538  {
1539    Attribute privilege = new BasicAttribute("ds-privilege-name");
1540    privilege.add("bypass-acl");
1541    privilege.add("modify-acl");
1542    privilege.add("config-read");
1543    privilege.add("config-write");
1544    privilege.add("ldif-import");
1545    privilege.add("ldif-export");
1546    privilege.add("backend-backup");
1547    privilege.add("backend-restore");
1548    privilege.add("server-shutdown");
1549    privilege.add("server-restart");
1550    privilege.add("disconnect-client");
1551    privilege.add("cancel-request");
1552    privilege.add("password-reset");
1553    privilege.add("update-schema");
1554    privilege.add("privilege-change");
1555    privilege.add("unindexed-search");
1556    privilege.add("subentry-write");
1557    privilege.add("changelog-read");
1558    return privilege;
1559  }
1560
1561  /**
1562   * Returns the attributes for some server properties.
1563   *
1564   * @param serverProperties
1565   *          the server properties.
1566   * @param addObjectClass
1567   *          Indicates if the object class has to be added.
1568   * @return the attributes for the given server properties.
1569   */
1570  private static BasicAttributes makeAttrsFromServerProperties(Map<ServerProperty, Object> serverProperties,
1571      boolean addObjectClass)
1572  {
1573    BasicAttributes result = new BasicAttributes();
1574
1575    // Transform 'properties' into 'attributes'
1576    for (ServerProperty prop : serverProperties.keySet())
1577    {
1578      Attribute attr = makeAttrFromServerProperty(prop, serverProperties.get(prop));
1579      if (attr != null)
1580      {
1581        result.put(attr);
1582      }
1583    }
1584    if (addObjectClass)
1585    {
1586      // Add the objectclass attribute value
1587      // TODO: use another structural objectclass
1588      Attribute oc = new BasicAttribute("objectclass");
1589      oc.add("top");
1590      oc.add("ds-cfg-branch");
1591      oc.add("extensibleobject");
1592      result.put(oc);
1593    }
1594    return result;
1595  }
1596
1597  /**
1598   * Returns the attribute for a given server property.
1599   *
1600   * @param property
1601   *          the server property.
1602   * @param value
1603   *          the value.
1604   * @return the attribute for a given server property.
1605   */
1606  private static Attribute makeAttrFromServerProperty(ServerProperty property, Object value)
1607  {
1608    Attribute result;
1609
1610    switch (property)
1611    {
1612    case INSTANCE_PUBLIC_KEY_CERTIFICATE:
1613      result = null; // used in separate instance key entry
1614      break;
1615    case GROUPS:
1616      result = new BasicAttribute(ServerProperty.GROUPS.getAttributeName());
1617      for (Object o : ((Set<?>) value))
1618      {
1619        result.add(o);
1620      }
1621      break;
1622    default:
1623      result = new BasicAttribute(property.getAttributeName(), value);
1624    }
1625    return result;
1626  }
1627
1628  /**
1629   * Returns the attributes for some server group properties.
1630   *
1631   * @param serverGroupProperties
1632   *          the server group properties.
1633   * @return the attributes for the given server group properties.
1634   */
1635  private static BasicAttributes makeAttrsFromServerGroupProperties(
1636      Map<ServerGroupProperty, Object> serverGroupProperties)
1637  {
1638    BasicAttributes result = new BasicAttributes();
1639
1640    // Transform 'properties' into 'attributes'
1641    for (ServerGroupProperty prop : serverGroupProperties.keySet())
1642    {
1643      Attribute attr = makeAttrFromServerGroupProperty(prop, serverGroupProperties.get(prop));
1644      if (attr != null)
1645      {
1646        result.put(attr);
1647      }
1648    }
1649    return result;
1650  }
1651
1652  /**
1653   * Returns the attributes for some server group properties.
1654   *
1655   * @param serverGroupProperties
1656   *          the server group properties.
1657   * @return the attributes for the given server group properties.
1658   */
1659  private static BasicAttributes makeAttrsFromServerGroupProperties(Set<ServerGroupProperty> serverGroupProperties)
1660  {
1661    BasicAttributes result = new BasicAttributes();
1662
1663    // Transform 'properties' into 'attributes'
1664    for (ServerGroupProperty prop : serverGroupProperties)
1665    {
1666      Attribute attr = makeAttrFromServerGroupProperty(prop, null);
1667      if (attr != null)
1668      {
1669        result.put(attr);
1670      }
1671    }
1672    return result;
1673  }
1674
1675  /**
1676   * Returns the attribute for a given server group property.
1677   *
1678   * @param property
1679   *          the server group property.
1680   * @param value
1681   *          the value.
1682   * @return the attribute for a given server group property.
1683   */
1684  private static Attribute makeAttrFromServerGroupProperty(ServerGroupProperty property, Object value)
1685  {
1686    switch (property)
1687    {
1688    case MEMBERS:
1689      Attribute result = new BasicAttribute(ServerGroupProperty.MEMBERS.getAttributeName());
1690      for (Object o : ((Set<?>) value))
1691      {
1692        result.add(o);
1693      }
1694      return result;
1695    default:
1696      return new BasicAttribute(property.getAttributeName(), value);
1697    }
1698  }
1699
1700  /**
1701   * Returns the properties of a server group for some LDAP attributes.
1702   *
1703   * @param attrs
1704   *          the LDAP attributes.
1705   * @return the properties of a server group for some LDAP attributes.
1706   * @throws ADSContextException
1707   *           if something goes wrong.
1708   */
1709  private Map<ServerGroupProperty, Object> makePropertiesFromServerGroupAttrs(Attributes attrs)
1710      throws ADSContextException
1711  {
1712    Map<ServerGroupProperty, Object> result = new HashMap<>();
1713    try
1714    {
1715      for (ServerGroupProperty prop : ServerGroupProperty.values())
1716      {
1717        Attribute attr = attrs.get(prop.getAttributeName());
1718        if (attr == null)
1719        {
1720          continue;
1721        }
1722        Object value;
1723
1724        if (attr.size() >= 1 && MULTIVALUED_SERVER_GROUP_PROPERTIES.contains(prop))
1725        {
1726          Set<String> set = new HashSet<>();
1727          NamingEnumeration<?> ae = attr.getAll();
1728          try
1729          {
1730            while (ae.hasMore())
1731            {
1732              set.add((String) ae.next());
1733            }
1734          }
1735          finally
1736          {
1737            ae.close();
1738          }
1739          value = set;
1740        }
1741        else
1742        {
1743          value = attr.get(0);
1744        }
1745
1746        result.put(prop, value);
1747      }
1748    }
1749    catch (NamingException x)
1750    {
1751      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
1752    }
1753    return result;
1754  }
1755
1756  /**
1757   * Returns the properties of a server for some LDAP attributes.
1758   *
1759   * @param attrs
1760   *          the LDAP attributes.
1761   * @return the properties of a server for some LDAP attributes.
1762   * @throws ADSContextException
1763   *           if something goes wrong.
1764   */
1765  private Map<ServerProperty, Object> makePropertiesFromServerAttrs(Attributes attrs) throws ADSContextException
1766  {
1767    Map<ServerProperty, Object> result = new HashMap<>();
1768    try
1769    {
1770      NamingEnumeration<? extends Attribute> ne = attrs.getAll();
1771      while (ne.hasMore())
1772      {
1773        Attribute attr = ne.next();
1774        String attrID = attr.getID();
1775        Object value;
1776
1777        if (attrID.endsWith(";binary"))
1778        {
1779          attrID = attrID.substring(0, attrID.lastIndexOf(";binary"));
1780        }
1781
1782        ServerProperty prop = null;
1783        ServerProperty[] props = ServerProperty.values();
1784        for (int i = 0; i < props.length && prop == null; i++)
1785        {
1786          String v = props[i].getAttributeName();
1787          if (attrID.equalsIgnoreCase(v))
1788          {
1789            prop = props[i];
1790          }
1791        }
1792        if (prop == null)
1793        {
1794          // Do not handle it
1795        }
1796        else
1797        {
1798          if (attr.size() >= 1 && MULTIVALUED_SERVER_PROPERTIES.contains(prop))
1799          {
1800            Set<String> set = new HashSet<>();
1801            NamingEnumeration<?> ae = attr.getAll();
1802            try
1803            {
1804              while (ae.hasMore())
1805              {
1806                set.add((String) ae.next());
1807              }
1808            }
1809            finally
1810            {
1811              ae.close();
1812            }
1813            value = set;
1814          }
1815          else
1816          {
1817            value = attr.get(0);
1818          }
1819
1820          result.put(prop, value);
1821        }
1822      }
1823    }
1824    catch (NamingException x)
1825    {
1826      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
1827    }
1828    return result;
1829  }
1830
1831  /**
1832   * Returns the properties of an administrator for some rdn and LDAP
1833   * attributes.
1834   *
1835   * @param rdn
1836   *          the RDN.
1837   * @param attrs
1838   *          the LDAP attributes.
1839   * @return the properties of an administrator for the given rdn and LDAP
1840   *         attributes.
1841   * @throws ADSContextException
1842   *           if something goes wrong.
1843   */
1844  private Map<AdministratorProperty, Object> makePropertiesFromAdministratorAttrs(String rdn, Attributes attrs)
1845      throws ADSContextException
1846  {
1847    Map<AdministratorProperty, Object> result = new HashMap<>();
1848    LdapName nameObj;
1849    nameObj = nameFromDN(rdn);
1850    String dn = nameObj + "," + getAdministratorContainerDN();
1851    result.put(AdministratorProperty.ADMINISTRATOR_DN, dn);
1852    NamingEnumeration<? extends Attribute> ne = null;
1853    try
1854    {
1855      ne = attrs.getAll();
1856      while (ne.hasMore())
1857      {
1858        Attribute attr = ne.next();
1859        String attrID = attr.getID();
1860        Object value;
1861
1862        if ("cn".equalsIgnoreCase(attrID))
1863        {
1864          value = attr.get(0);
1865          result.put(AdministratorProperty.UID, value);
1866        }
1867        else if ("userpassword".equalsIgnoreCase(attrID))
1868        {
1869          value = new String((byte[]) attr.get());
1870          result.put(AdministratorProperty.PASSWORD, value);
1871        }
1872        else if ("description".equalsIgnoreCase(attrID))
1873        {
1874          value = attr.get(0);
1875          result.put(AdministratorProperty.DESCRIPTION, value);
1876        }
1877        else if ("ds-privilege-name".equalsIgnoreCase(attrID))
1878        {
1879          LinkedHashSet<String> privileges = new LinkedHashSet<>();
1880          NamingEnumeration<?> attValueList = attr.getAll();
1881          while (attValueList.hasMoreElements())
1882          {
1883            privileges.add(attValueList.next().toString());
1884          }
1885          result.put(AdministratorProperty.PRIVILEGE, privileges);
1886        }
1887      }
1888    }
1889    catch (NamingException x)
1890    {
1891      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
1892    }
1893    finally
1894    {
1895      handleCloseNamingEnumeration(ne);
1896    }
1897
1898    return result;
1899  }
1900
1901  /**
1902   * Returns the parent entry of the server entries.
1903   *
1904   * @return the parent entry of the server entries.
1905   */
1906  public static String getServerContainerDN()
1907  {
1908    return "cn=Servers," + getAdministrationSuffixDN();
1909  }
1910
1911  /**
1912   * Returns the parent entry of the administrator entries.
1913   *
1914   * @return the parent entry of the administrator entries.
1915   */
1916  public static String getAdministratorContainerDN()
1917  {
1918    return "cn=Administrators," + getAdministrationSuffixDN();
1919  }
1920
1921  /**
1922   * Returns the parent entry of the server group entries.
1923   *
1924   * @return the parent entry of the server group entries.
1925   */
1926  public static String getServerGroupContainerDN()
1927  {
1928    return "cn=Server Groups," + getAdministrationSuffixDN();
1929  }
1930
1931  /**
1932   * Returns the all server group entry DN.
1933   *
1934   * @return the all server group entry DN.
1935   */
1936  private static String getAllServerGroupDN()
1937  {
1938    return "cn=" + Rdn.escapeValue(ALL_SERVERGROUP_NAME) + "," + getServerGroupContainerDN();
1939  }
1940
1941  /**
1942   * Returns the host name for the given properties.
1943   *
1944   * @param serverProperties
1945   *          the server properties.
1946   * @return the host name for the given properties.
1947   * @throws ADSContextException
1948   *           if the host name could not be found or its value is not valid.
1949   */
1950  private static String getHostname(Map<ServerProperty, Object> serverProperties) throws ADSContextException
1951  {
1952    String result = (String) serverProperties.get(ServerProperty.HOST_NAME);
1953    if (result == null)
1954    {
1955      throw new ADSContextException(ErrorType.MISSING_HOSTNAME);
1956    }
1957    else if (result.length() == 0)
1958    {
1959      throw new ADSContextException(ErrorType.NOVALID_HOSTNAME);
1960    }
1961    return result;
1962  }
1963
1964  /**
1965   * Returns the Server ID for the given properties.
1966   *
1967   * @param serverProperties
1968   *          the server properties.
1969   * @return the server ID for the given properties or null.
1970   */
1971  private static String getServerID(Map<ServerProperty, Object> serverProperties)
1972  {
1973    String result = (String) serverProperties.get(ServerProperty.ID);
1974    if (result != null && result.length() == 0)
1975    {
1976      result = null;
1977    }
1978    return result;
1979  }
1980
1981  /**
1982   * Returns the install path for the given properties.
1983   *
1984   * @param serverProperties
1985   *          the server properties.
1986   * @return the install path for the given properties.
1987   * @throws ADSContextException
1988   *           if the install path could not be found or its value is not valid.
1989   */
1990  private static String getInstallPath(Map<ServerProperty, Object> serverProperties) throws ADSContextException
1991  {
1992    String result = (String) serverProperties.get(ServerProperty.INSTANCE_PATH);
1993    if (result == null)
1994    {
1995      throw new ADSContextException(ErrorType.MISSING_IPATH);
1996    }
1997    else if (result.length() == 0)
1998    {
1999      throw new ADSContextException(ErrorType.NOVALID_IPATH);
2000    }
2001    return result;
2002  }
2003
2004  /**
2005   * Returns the Administrator UID for the given properties.
2006   *
2007   * @param adminProperties
2008   *          the server properties.
2009   * @return the Administrator UID for the given properties.
2010   * @throws ADSContextException
2011   *           if the administrator UID could not be found.
2012   */
2013  private static String getAdministratorUID(Map<AdministratorProperty, Object> adminProperties)
2014      throws ADSContextException
2015  {
2016    String result = (String) adminProperties.get(AdministratorProperty.UID);
2017    if (result == null)
2018    {
2019      throw new ADSContextException(ErrorType.MISSING_ADMIN_UID);
2020    }
2021    return result;
2022  }
2023
2024  /**
2025   * Returns the Administrator password for the given properties.
2026   *
2027   * @param adminProperties
2028   *          the server properties.
2029   * @return the Administrator password for the given properties.
2030   * @throws ADSContextException
2031   *           if the administrator password could not be found.
2032   */
2033  private static String getAdministratorPassword(Map<AdministratorProperty, Object> adminProperties)
2034      throws ADSContextException
2035  {
2036    String result = (String) adminProperties.get(AdministratorProperty.PASSWORD);
2037    if (result == null)
2038    {
2039      throw new ADSContextException(ErrorType.MISSING_ADMIN_PASSWORD);
2040    }
2041    return result;
2042  }
2043
2044  // LDAP utilities
2045  /**
2046   * Returns the LdapName object for the given dn.
2047   *
2048   * @param dn
2049   *          the DN.
2050   * @return the LdapName object for the given dn.
2051   * @throws ADSContextException
2052   *           if a valid LdapName could not be retrieved for the given dn.
2053   */
2054  private static LdapName nameFromDN(String dn) throws ADSContextException
2055  {
2056    try
2057    {
2058      return new LdapName(dn);
2059    }
2060    catch (InvalidNameException x)
2061    {
2062      logger.error(LocalizableMessage.raw("Error parsing dn " + dn, x));
2063      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
2064    }
2065  }
2066
2067  /**
2068   * Returns the String rdn for the given search result name.
2069   *
2070   * @param rdnName
2071   *          the search result name.
2072   * @return the String rdn for the given search result name.
2073   * @throws ADSContextException
2074   *           if a valid String rdn could not be retrieved for the given result
2075   *           name.
2076   */
2077  private static String getRdn(String rdnName) throws ADSContextException
2078  {
2079    // Transform the JNDI name into a RDN string
2080    try
2081    {
2082      return new CompositeName(rdnName).get(0);
2083    }
2084    catch (InvalidNameException x)
2085    {
2086      logger.error(LocalizableMessage.raw("Error parsing rdn " + rdnName, x));
2087      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
2088    }
2089  }
2090
2091  /**
2092   * Tells whether an entry with the provided DN exists.
2093   *
2094   * @param dn
2095   *          the DN to check.
2096   * @return <CODE>true</CODE> if the entry exists and <CODE>false</CODE> if it
2097   *         does not.
2098   * @throws ADSContextException
2099   *           if an error occurred while checking if the entry exists or not.
2100   */
2101  private boolean isExistingEntry(LdapName dn) throws ADSContextException
2102  {
2103    try
2104    {
2105      SearchControls sc = new SearchControls();
2106
2107      sc.setSearchScope(SearchControls.OBJECT_SCOPE);
2108      sc.setReturningAttributes(new String[] { SchemaConstants.NO_ATTRIBUTES });
2109      NamingEnumeration<SearchResult> sr = getDirContext().search(dn, "(objectclass=*)", sc);
2110      boolean result = false;
2111      try
2112      {
2113        while (sr.hasMore())
2114        {
2115          sr.next();
2116          result = true;
2117        }
2118      }
2119      finally
2120      {
2121        sr.close();
2122      }
2123      return result;
2124    }
2125    catch (NameNotFoundException x)
2126    {
2127      return false;
2128    }
2129    catch (NoPermissionException x)
2130    {
2131      throw new ADSContextException(ErrorType.ACCESS_PERMISSION);
2132    }
2133    catch (javax.naming.NamingException x)
2134    {
2135      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
2136    }
2137  }
2138
2139  /**
2140   * Creates a container entry with the given dn.
2141   *
2142   * @param dn
2143   *          the entry of the new entry to be created.
2144   * @throws ADSContextException
2145   *           if the entry could not be created.
2146   */
2147  private void createContainerEntry(String dn) throws ADSContextException
2148  {
2149    BasicAttributes attrs = new BasicAttributes();
2150    Attribute oc = new BasicAttribute("objectclass");
2151    oc.add("top");
2152    oc.add("ds-cfg-branch");
2153    attrs.put(oc);
2154    createEntry(dn, attrs);
2155  }
2156
2157  /**
2158   * Creates the administrator container entry.
2159   *
2160   * @throws ADSContextException
2161   *           if the entry could not be created.
2162   */
2163  private void createAdministratorContainerEntry() throws ADSContextException
2164  {
2165    BasicAttributes attrs = new BasicAttributes();
2166    Attribute oc = new BasicAttribute("objectclass");
2167    oc.add("groupofurls");
2168    attrs.put(oc);
2169    attrs.put("memberURL", "ldap:///" + getAdministratorContainerDN() + "??one?(objectclass=*)");
2170    attrs.put("description", "Group of identities which have full access.");
2171    createEntry(getAdministratorContainerDN(), attrs);
2172  }
2173
2174  /**
2175   * Creates the top container entry.
2176   *
2177   * @throws ADSContextException
2178   *           if the entry could not be created.
2179   */
2180  private void createTopContainerEntry() throws ADSContextException
2181  {
2182    BasicAttributes attrs = new BasicAttributes();
2183    Attribute oc = new BasicAttribute("objectclass");
2184    oc.add("top");
2185    oc.add("ds-cfg-branch");
2186    attrs.put(oc);
2187    createEntry(getAdministrationSuffixDN(), attrs);
2188  }
2189
2190  /**
2191   * Creates an entry with the provided dn and attributes.
2192   *
2193   * @param dn
2194   *          the dn of the entry.
2195   * @param attrs
2196   *          the attributes of the entry.
2197   * @throws ADSContextException
2198   *           if the entry could not be created.
2199   */
2200  private void createEntry(String dn, Attributes attrs) throws ADSContextException
2201  {
2202    try
2203    {
2204      DirContext ctx = getDirContext().createSubcontext(nameFromDN(dn), attrs);
2205      ctx.close();
2206    }
2207    catch (NamingException x)
2208    {
2209      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
2210    }
2211  }
2212
2213  /**
2214   * Creates the Administration Suffix.
2215   *
2216   * @param backendName
2217   *          the backend name to be used for the Administration Suffix. If this
2218   *          value is null the default backendName for the Administration
2219   *          Suffix will be used.
2220   * @throws ADSContextException
2221   *           if something goes wrong.
2222   */
2223  public void createAdministrationSuffix(String backendName) throws ADSContextException
2224  {
2225    ADSContextHelper helper = new ADSContextHelper();
2226    String ben = backendName;
2227    if (backendName == null)
2228    {
2229      ben = getDefaultBackendName();
2230    }
2231    helper.createAdministrationSuffix(getDirContext(), ben);
2232  }
2233
2234  /**
2235   * Returns the default backend name of the administration data.
2236   *
2237   * @return the default backend name of the administration data.
2238   */
2239  public static String getDefaultBackendName()
2240  {
2241    return "adminRoot";
2242  }
2243
2244  /**
2245   * Returns the LDIF file of the administration data.
2246   *
2247   * @return the LDIF file of the administration data.
2248   */
2249  public static String getAdminLDIFFile()
2250  {
2251    return "config" + File.separator + "admin-backend.ldif";
2252  }
2253
2254  /** CryptoManager related types, fields, and methods. */
2255
2256  /**
2257   * Returns the parent entry of the server key entries in ADS.
2258   *
2259   * @return the parent entry of the server key entries in ADS.
2260   */
2261  public static String getInstanceKeysContainerDN()
2262  {
2263    return "cn=instance keys," + getAdministrationSuffixDN();
2264  }
2265
2266  /**
2267   * Returns the parent entry of the secret key entries in ADS.
2268   *
2269   * @return the parent entry of the secret key entries in ADS.
2270   */
2271  public static String getSecretKeysContainerDN()
2272  {
2273    return "cn=secret keys," + getAdministrationSuffixDN();
2274  }
2275
2276  /**
2277   * Tells whether the provided server is registered in the registry.
2278   *
2279   * @param server
2280   *          the server.
2281   * @param registry
2282   *          the registry.
2283   * @return <CODE>true</CODE> if the server is registered in the registry and
2284   *         <CODE>false</CODE> otherwise.
2285   */
2286  public static boolean isRegistered(ServerDescriptor server, Set<Map<ADSContext.ServerProperty, Object>> registry)
2287  {
2288    for (Map<ADSContext.ServerProperty, Object> s : registry)
2289    {
2290      ServerDescriptor servInRegistry = ServerDescriptor.createStandalone(s);
2291      if (servInRegistry.getId().equals(server.getId()))
2292      {
2293        return true;
2294      }
2295    }
2296    return false;
2297  }
2298
2299  /**
2300   * Register instance key-pair public-key certificate provided in
2301   * serverProperties: generate a key-id attribute if one is not provided (as
2302   * expected); add an instance key public-key certificate entry for the key
2303   * certificate; and associate the certificate entry with the server entry via
2304   * the key ID attribute.
2305   *
2306   * @param serverProperties
2307   *          Properties of the server being registered to which the instance
2308   *          key entry belongs.
2309   * @param serverEntryDn
2310   *          The server's ADS entry DN.
2311   * @throws NamingException
2312   *           In case some JNDI operation fails.
2313   * @throws CryptoManager.CryptoManagerException
2314   *           In case there is a problem getting the instance public key
2315   *           certificate ID.
2316   */
2317  private void registerInstanceKeyCertificate(Map<ServerProperty, Object> serverProperties, LdapName serverEntryDn)
2318      throws ADSContextException
2319  {
2320    ADSContextHelper helper = new ADSContextHelper();
2321    helper.registerInstanceKeyCertificate(dirContext, serverProperties, serverEntryDn);
2322  }
2323
2324  /**
2325   * Return the set of valid (i.e., not tagged as compromised) instance key-pair
2326   * public-key certificate entries in ADS. NOTE: calling this method assumes
2327   * that all the jar files are present in the classpath.
2328   *
2329   * @return The set of valid (i.e., not tagged as compromised) instance
2330   *         key-pair public-key certificate entries in ADS represented as a Map
2331   *         from ds-cfg-key-id value to ds-cfg-public-key-certificate;binary
2332   *         value. Note that the collection might be empty.
2333   * @throws ADSContextException
2334   *           in case of problems with the entry search.
2335   * @see org.opends.server.crypto.CryptoManagerImpl#getTrustedCertificates
2336   */
2337  public Map<String, byte[]> getTrustedCertificates() throws ADSContextException
2338  {
2339    final Map<String, byte[]> certificateMap = new HashMap<>();
2340    final String baseDNStr = getInstanceKeysContainerDN();
2341    try
2342    {
2343      ADSContextHelper helper = new ADSContextHelper();
2344      final LdapName baseDN = new LdapName(baseDNStr);
2345      final String FILTER_OC_INSTANCE_KEY = "(objectclass=" + helper.getOcCryptoInstanceKey() + ")";
2346      final String FILTER_NOT_COMPROMISED = "(!(" + helper.getAttrCryptoKeyCompromisedTime() + "=*))";
2347      final String searchFilter = "(&" + FILTER_OC_INSTANCE_KEY + FILTER_NOT_COMPROMISED + ")";
2348      final SearchControls searchControls = new SearchControls();
2349      searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
2350      final String attrIDs[] =
2351          { ADSContext.ServerProperty.INSTANCE_KEY_ID.getAttributeName(),
2352            ADSContext.ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE.getAttributeName() + ";binary" };
2353      searchControls.setReturningAttributes(attrIDs);
2354      NamingEnumeration<SearchResult> keyEntries = dirContext.search(baseDN, searchFilter, searchControls);
2355      try
2356      {
2357        while (keyEntries.hasMore())
2358        {
2359          final SearchResult entry = keyEntries.next();
2360          final Attributes attrs = entry.getAttributes();
2361          final Attribute keyIDAttr = attrs.get(attrIDs[0]);
2362          final Attribute keyCertAttr = attrs.get(attrIDs[1]);
2363          if (null == keyIDAttr || null == keyCertAttr)
2364          {
2365            continue;// schema viol.
2366          }
2367          certificateMap.put((String) keyIDAttr.get(), (byte[]) keyCertAttr.get());
2368        }
2369      }
2370      finally
2371      {
2372        try
2373        {
2374          keyEntries.close();
2375        }
2376        catch (Exception ex)
2377        {
2378          logger.warn(LocalizableMessage.raw("Unexpected error closing enumeration on ADS key pairs", ex));
2379        }
2380      }
2381    }
2382    catch (NamingException x)
2383    {
2384      throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, x);
2385    }
2386    return certificateMap;
2387  }
2388
2389  /**
2390   * Merge the contents of this ADSContext with the contents of the provided
2391   * ADSContext. Note that only the contents of this ADSContext will be updated.
2392   *
2393   * @param adsCtx
2394   *          the other ADSContext to merge the contents with.
2395   * @throws ADSContextException
2396   *           if there was an error during the merge.
2397   */
2398  public void mergeWithRegistry(ADSContext adsCtx) throws ADSContextException
2399  {
2400    try
2401    {
2402      mergeAdministrators(adsCtx);
2403      mergeServerGroups(adsCtx);
2404      mergeServers(adsCtx);
2405    }
2406    catch (ADSContextException adce)
2407    {
2408      LocalizableMessage msg = ERR_ADS_MERGE.get(ConnectionUtils.getHostPort(getDirContext()),
2409          ConnectionUtils.getHostPort(adsCtx.getDirContext()), adce.getMessageObject());
2410      throw new ADSContextException(ErrorType.ERROR_MERGING, msg, adce);
2411    }
2412  }
2413
2414  /**
2415   * Merge the administrator contents of this ADSContext with the contents of
2416   * the provided ADSContext. Note that only the contents of this ADSContext
2417   * will be updated.
2418   *
2419   * @param adsCtx
2420   *          the other ADSContext to merge the contents with.
2421   * @throws ADSContextException
2422   *           if there was an error during the merge.
2423   */
2424  private void mergeAdministrators(ADSContext adsCtx) throws ADSContextException
2425  {
2426    Set<Map<AdministratorProperty, Object>> admins2 = adsCtx.readAdministratorRegistry();
2427    SortedSet<String> notDefinedAdmins = new TreeSet<>();
2428    for (Map<AdministratorProperty, Object> admin2 : admins2)
2429    {
2430      String uid = (String) admin2.get(AdministratorProperty.UID);
2431      if (!isAdministratorAlreadyRegistered(uid))
2432      {
2433        notDefinedAdmins.add(uid);
2434      }
2435    }
2436    if (!notDefinedAdmins.isEmpty())
2437    {
2438      LocalizableMessage msg = ERR_ADS_ADMINISTRATOR_MERGE.get(
2439          ConnectionUtils.getHostPort(adsCtx.getDirContext()), ConnectionUtils.getHostPort(getDirContext()),
2440          joinAsString(Constants.LINE_SEPARATOR, notDefinedAdmins), ConnectionUtils.getHostPort(getDirContext()));
2441      throw new ADSContextException(ErrorType.ERROR_MERGING, msg, null);
2442    }
2443  }
2444
2445  /**
2446   * Merge the groups contents of this ADSContext with the contents of the
2447   * provided ADSContext. Note that only the contents of this ADSContext will be
2448   * updated.
2449   *
2450   * @param adsCtx
2451   *          the other ADSContext to merge the contents with.
2452   * @throws ADSContextException
2453   *           if there was an error during the merge.
2454   */
2455  private void mergeServerGroups(ADSContext adsCtx) throws ADSContextException
2456  {
2457    Set<Map<ServerGroupProperty, Object>> serverGroups1 = readServerGroupRegistry();
2458    Set<Map<ServerGroupProperty, Object>> serverGroups2 = adsCtx.readServerGroupRegistry();
2459
2460    for (Map<ServerGroupProperty, Object> group2 : serverGroups2)
2461    {
2462      Map<ServerGroupProperty, Object> group1 = null;
2463      String uid2 = (String) group2.get(ServerGroupProperty.UID);
2464      for (Map<ServerGroupProperty, Object> gr : serverGroups1)
2465      {
2466        String uid1 = (String) gr.get(ServerGroupProperty.UID);
2467        if (uid1.equalsIgnoreCase(uid2))
2468        {
2469          group1 = gr;
2470          break;
2471        }
2472      }
2473
2474      if (group1 != null)
2475      {
2476        // Merge the members, keep the description on this ADS.
2477        Set<String> member1List = getServerGroupMemberList(uid2);
2478        if (member1List == null)
2479        {
2480          member1List = new HashSet<>();
2481        }
2482        Set<String> member2List = adsCtx.getServerGroupMemberList(uid2);
2483        if (member2List != null && !member2List.isEmpty())
2484        {
2485          member1List.addAll(member2List);
2486          Map<ServerGroupProperty, Object> newProperties = new HashMap<>();
2487          newProperties.put(ServerGroupProperty.MEMBERS, member1List);
2488          updateServerGroup(uid2, newProperties);
2489        }
2490      }
2491      else
2492      {
2493        createServerGroup(group2);
2494      }
2495    }
2496  }
2497
2498  /**
2499   * Merge the server contents of this ADSContext with the contents of the
2500   * provided ADSContext. Note that only the contents of this ADSContext will be
2501   * updated.
2502   *
2503   * @param adsCtx
2504   *          the other ADSContext to merge the contents with.
2505   * @throws ADSContextException
2506   *           if there was an error during the merge.
2507   */
2508  private void mergeServers(ADSContext adsCtx) throws ADSContextException
2509  {
2510    for (Map<ServerProperty, Object> server2 : adsCtx.readServerRegistry())
2511    {
2512      if (!isServerAlreadyRegistered(server2))
2513      {
2514        registerServer(server2);
2515      }
2516    }
2517  }
2518
2519  private void handleCloseNamingEnumeration(NamingEnumeration<?> ne) throws ADSContextException
2520  {
2521    if (ne != null)
2522    {
2523      try
2524      {
2525        ne.close();
2526      }
2527      catch (NamingException ex)
2528      {
2529        throw new ADSContextException(ErrorType.ERROR_UNEXPECTED, ex);
2530      }
2531    }
2532  }
2533}