/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
* or http://forgerock.org/license/CDDLv1.0.html.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at legal-notices/CDDLv1_0.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2008 Sun Microsystems, Inc.
* Portions Copyright 2012-2015 ForgeRock AS
*/
package org.opends.server.admin;
import java.net.InetAddress;
import java.util.LinkedList;
import java.util.List;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ModificationType;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.internal.Requests;
import org.opends.server.protocols.internal.SearchRequest;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.Attributes;
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.Modification;
import org.opends.server.types.SearchResultEntry;
import static org.opends.server.protocols.internal.Requests.*;
/**
* Check if information found in "cn=admin data" is coherent with
* cn=config. If and inconsistency is detected, we log a warning
* message and update "cn=admin data"
*/
public final class AdministrationDataSync
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
/** The root connection. */
private InternalClientConnection internalConnection;
/** The attribute name used to store the port. TODO Use the default one. */
private static final String LDAP_PORT = "ds-cfg-listen-port";
/**
* Create an object that will synchronize configuration and the admin data.
*
* @param internalConnection
* The root connection.
*/
public AdministrationDataSync(InternalClientConnection internalConnection)
{
this.internalConnection = internalConnection;
}
/**
* Check if information found in "cn=admin data" is coherent with
* cn=config. If and inconsistency is detected, we log a warning
* message and update "cn=admin data"
*/
public void synchronize()
{
// Check if the admin connector is in sync
checkAdminConnector();
}
/**
* Check if the admin connector is in sync. The desynchronization
* could occurs after the upgrade from 1.0.
*/
private void checkAdminConnector()
{
// Look for the server registration in "cn=admin data"
DN serverEntryDN = searchServerEntry();
if (serverEntryDN == null)
{
// Nothing to do
return;
}
// Get the admin port
String adminPort = getAttr("cn=Administration Connector,cn=config", LDAP_PORT);
if (adminPort == null)
{
// best effort.
return;
}
AttributeType attrType1 = DirectoryServer.getAttributeTypeOrDefault("adminport".toLowerCase());
AttributeType attrType2 = DirectoryServer.getAttributeTypeOrDefault("adminEnabled".toLowerCase());
LinkedList<Modification> mods = new LinkedList<>();
mods.add(new Modification(ModificationType.REPLACE, Attributes.create(attrType1, adminPort)));
mods.add(new Modification(ModificationType.REPLACE, Attributes.create(attrType2, "true")));
// Process modification
internalConnection.processModify(serverEntryDN, mods);
}
/**
* Look for the DN of the local register server. Assumption: default
* Connection Handler naming is used.
*
* @return The DN of the local register server or null.
*/
private DN searchServerEntry()
{
DN returnDN = null;
// Get the LDAP and LDAPS port
String ldapPort = getAttr("cn=LDAP Connection Handler,cn=Connection Handlers,cn=config", LDAP_PORT);
String ldapsPort = getAttr("cn=LDAPS Connection Handler,cn=Connection Handlers,cn=config", LDAP_PORT);
boolean ldapsPortEnable = false;
String val = getAttr("cn=LDAPS Connection Handler,cn=Connection Handlers,cn=config", "ds-cfg-enabled");
if (val != null)
{
ldapsPortEnable = "true".equals(val.toLowerCase());
}
if (ldapPort == null && ldapsPort == null)
{
// best effort (see assumption)
return null;
}
// Get the IP address of the local host.
String hostName;
try
{
hostName = InetAddress.getLocalHost().getCanonicalHostName();
}
catch (Throwable t)
{
// best effort.
return null;
}
// Look for a local server with the Ldap Port.
try
{
SearchRequest request = newSearchRequest(DN.valueOf("cn=Servers,cn=admin data"), SearchScope.SINGLE_LEVEL);
InternalSearchOperation op = internalConnection.processSearch(request);
if (op.getResultCode() == ResultCode.SUCCESS)
{
Entry entry = findSameHostAndPort(op.getSearchEntries(), hostName, ldapPort, ldapsPortEnable, ldapsPort);
if (entry != null)
{
returnDN = entry.getName();
}
}
}
catch (DirectoryException e)
{
// never happens because the filter is always valid.
return null;
}
return returnDN;
}
private Entry findSameHostAndPort(LinkedList<SearchResultEntry> searchResultEntries,
String hostName, String ldapPort, boolean ldapsPortEnable, String ldapsPort)
{
for (Entry currentEntry : searchResultEntries)
{
String currentHostname = currentEntry.parseAttribute("hostname").asString();
try
{
String currentIPAddress = InetAddress.getByName(currentHostname).getCanonicalHostName();
if (currentIPAddress.equals(hostName))
{
// Check if one of the port match
String currentport = currentEntry.parseAttribute("ldapport").asString();
if (currentport.equals(ldapPort))
{
return currentEntry;
}
if (ldapsPortEnable)
{
currentport = currentEntry.parseAttribute("ldapsport").asString();
if (currentport.equals(ldapsPort))
{
return currentEntry;
}
}
}
}
catch (Exception e)
{
// best effort.
continue;
}
}
return null;
}
/**
* Gets an attribute value from an entry.
*
* @param DN
* The DN of the entry.
* @param attrName
* The attribute name.
* @return The attribute value or {@code null} if the value could
* not be retrieved.
*/
private String getAttr(String baseDN, String attrName)
{
InternalSearchOperation search;
try
{
SearchRequest request = Requests.newSearchRequest(DN.valueOf(baseDN), SearchScope.BASE_OBJECT)
.addAttribute(attrName);
search = internalConnection.processSearch(request);
if (search.getResultCode() != ResultCode.SUCCESS)
{
// can not happen
// best effort.
// TODO Log an Error.
return null;
}
}
catch (DirectoryException e)
{
// can not happen
// best effort.
logger.traceException(e);
return null;
}
// Read the port from the PORT attribute
SearchResultEntry adminConnectorEntry = null;
LinkedList<SearchResultEntry> result = search.getSearchEntries();
if (!result.isEmpty())
{
adminConnectorEntry = result.getFirst();
}
AttributeType attrType = DirectoryServer.getAttributeTypeOrDefault(attrName);
List<Attribute> attrs = adminConnectorEntry.getAttribute(attrType);
if (attrs != null)
{
// Get the attribute value
return attrs.get(0).iterator().next().toString();
}
// can not happen
// best effort.
// TODO Log an Error.
return null;
}
}