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