001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2008 Sun Microsystems, Inc.
015 * Portions Copyright 2012-2016 ForgeRock AS.
016 */
017package org.opends.server.admin;
018
019import static org.opends.server.protocols.internal.Requests.*;
020
021import java.net.InetAddress;
022import java.util.LinkedList;
023import java.util.List;
024
025import org.forgerock.i18n.slf4j.LocalizedLogger;
026import org.forgerock.opendj.ldap.DN;
027import org.forgerock.opendj.ldap.ModificationType;
028import org.forgerock.opendj.ldap.ResultCode;
029import org.forgerock.opendj.ldap.SearchScope;
030import org.forgerock.opendj.ldap.schema.AttributeType;
031import org.opends.server.core.DirectoryServer;
032import org.opends.server.protocols.internal.InternalClientConnection;
033import org.opends.server.protocols.internal.InternalSearchOperation;
034import org.opends.server.protocols.internal.Requests;
035import org.opends.server.protocols.internal.SearchRequest;
036import org.opends.server.types.Attribute;
037import org.opends.server.types.Attributes;
038import org.opends.server.types.Entry;
039import org.opends.server.types.Modification;
040import org.opends.server.types.SearchResultEntry;
041
042/**
043 * Check if information found in "cn=admin data" is coherent with
044 * cn=config. If and inconsistency is detected, we log a warning
045 * message and update "cn=admin data"
046 */
047public final class AdministrationDataSync
048{
049  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
050
051  /** The root connection. */
052  private final InternalClientConnection internalConnection;
053
054  /** The attribute name used to store the port. TODO Use the default one. */
055  private static final String LDAP_PORT = "ds-cfg-listen-port";
056
057  /**
058   * Create an object that will synchronize configuration and the admin data.
059   *
060   * @param internalConnection
061   *          The root connection.
062   */
063  public AdministrationDataSync(InternalClientConnection internalConnection)
064  {
065    this.internalConnection = internalConnection;
066  }
067
068  /**
069   * Check if information found in "cn=admin data" is coherent with
070   * cn=config. If and inconsistency is detected, we log a warning
071   * message and update "cn=admin data"
072   */
073  public void synchronize()
074  {
075    // Check if the admin connector is in sync
076    checkAdminConnector();
077  }
078
079  /**
080   * Check if the admin connector is in sync. The desynchronization
081   * could occurs after the upgrade from 1.0.
082   */
083  private void checkAdminConnector()
084  {
085    // Look for the server registration in "cn=admin data"
086    DN serverEntryDN = searchServerEntry();
087    if (serverEntryDN == null)
088    {
089      // Nothing to do
090      return;
091    }
092
093    // Get the admin port
094    String adminPort = getAttr("cn=Administration Connector,cn=config", LDAP_PORT);
095    if (adminPort == null)
096    {
097      // best effort.
098      return;
099    }
100
101    AttributeType attrType1 = DirectoryServer.getSchema().getAttributeType("adminport");
102    AttributeType attrType2 = DirectoryServer.getSchema().getAttributeType("adminEnabled");
103
104    LinkedList<Modification> mods = new LinkedList<>();
105    mods.add(new Modification(ModificationType.REPLACE, Attributes.create(attrType1, adminPort)));
106    mods.add(new Modification(ModificationType.REPLACE, Attributes.create(attrType2, "true")));
107
108    // Process modification
109    internalConnection.processModify(serverEntryDN, mods);
110  }
111
112  /**
113   * Look for the DN of the local register server. Assumption: default
114   * Connection Handler naming is used.
115   *
116   * @return The DN of the local register server or null.
117   */
118  private DN searchServerEntry()
119  {
120    // Get the LDAP and LDAPS port
121    String ldapPort = getAttr("cn=LDAP Connection Handler,cn=Connection Handlers,cn=config", LDAP_PORT);
122    String ldapsPort = getAttr("cn=LDAPS Connection Handler,cn=Connection Handlers,cn=config", LDAP_PORT);
123    boolean ldapsPortEnable = false;
124    String val = getAttr("cn=LDAPS Connection Handler,cn=Connection Handlers,cn=config", "ds-cfg-enabled");
125    if (val != null)
126    {
127      ldapsPortEnable = "true".equalsIgnoreCase(val);
128    }
129    if (ldapPort == null && ldapsPort == null)
130    {
131      // best effort (see assumption)
132      return null;
133    }
134
135    // Get the IP address of the local host.
136    String hostName;
137    try
138    {
139      hostName = InetAddress.getLocalHost().getCanonicalHostName();
140    }
141    catch (Throwable t)
142    {
143      // best effort.
144      return null;
145    }
146
147    // Look for a local server with the Ldap Port.
148    SearchRequest request = newSearchRequest(DN.valueOf("cn=Servers,cn=admin data"), SearchScope.SINGLE_LEVEL);
149    InternalSearchOperation op = internalConnection.processSearch(request);
150    if (op.getResultCode() == ResultCode.SUCCESS)
151    {
152      Entry entry = findSameHostAndPort(op.getSearchEntries(), hostName, ldapPort, ldapsPortEnable, ldapsPort);
153      if (entry != null)
154      {
155        return entry.getName();
156      }
157    }
158    return null;
159  }
160
161  private Entry findSameHostAndPort(LinkedList<SearchResultEntry> searchResultEntries,
162      String hostName, String ldapPort, boolean ldapsPortEnable, String ldapsPort)
163  {
164    for (Entry currentEntry : searchResultEntries)
165    {
166      String currentHostname = currentEntry.parseAttribute("hostname").asString();
167      try
168      {
169        String currentIPAddress = InetAddress.getByName(currentHostname).getCanonicalHostName();
170        if (currentIPAddress.equals(hostName))
171        {
172          // Check if one of the port match
173          String currentport = currentEntry.parseAttribute("ldapport").asString();
174          if (currentport.equals(ldapPort))
175          {
176            return currentEntry;
177          }
178          if (ldapsPortEnable)
179          {
180            currentport = currentEntry.parseAttribute("ldapsport").asString();
181            if (currentport.equals(ldapsPort))
182            {
183              return currentEntry;
184            }
185          }
186        }
187      }
188      catch (Exception e)
189      {
190        // best effort.
191        continue;
192      }
193    }
194    return null;
195  }
196
197  /**
198   * Gets an attribute value from an entry.
199   *
200   * @param DN
201   *          The DN of the entry.
202   * @param attrName
203   *          The attribute name.
204   * @return The attribute value or {@code null} if the value could
205   *         not be retrieved.
206   */
207  private String getAttr(String baseDN, String attrName)
208  {
209    SearchRequest request = Requests.newSearchRequest(DN.valueOf(baseDN), SearchScope.BASE_OBJECT)
210        .addAttribute(attrName);
211    InternalSearchOperation search = internalConnection.processSearch(request);
212    if (search.getResultCode() != ResultCode.SUCCESS)
213    {
214      // can not happen
215      // best effort.
216      // TODO Log an Error.
217      return null;
218    }
219
220    // Read the port from the PORT attribute
221    LinkedList<SearchResultEntry> result = search.getSearchEntries();
222    if (!result.isEmpty())
223    {
224      SearchResultEntry adminConnectorEntry = result.getFirst();
225      AttributeType attrType = DirectoryServer.getSchema().getAttributeType(attrName);
226      List<Attribute> attrs = adminConnectorEntry.getAttribute(attrType);
227      if (!attrs.isEmpty())
228      {
229        // Get the attribute value
230        return attrs.get(0).iterator().next().toString();
231      }
232    }
233
234    // Can not happen. Best effort.
235    // TODO Log an Error.
236    return null;
237  }
238}