/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * Copyright 2007-2010 Sun Microsystems, Inc. * Portions Copyright 2013-2016 ForgeRock AS. */ package org.opends.admin.ads; import static org.forgerock.opendj.ldap.SearchScope.*; import static org.forgerock.opendj.ldap.requests.Requests.*; import static org.opends.admin.ads.util.ConnectionUtils.*; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import javax.naming.ldap.Rdn; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.ldap.Attribute; import org.forgerock.opendj.ldap.Connection; import org.forgerock.opendj.ldap.DN; import org.forgerock.opendj.ldap.LdapException; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.requests.AddRequest; import org.forgerock.opendj.ldap.requests.SearchRequest; import org.forgerock.opendj.ldap.responses.SearchResultEntry; import org.forgerock.opendj.ldif.ConnectionEntryReader; import org.forgerock.util.Pair; import org.opends.admin.ads.util.ConnectionWrapper; import org.opends.quicksetup.Constants; import org.opends.server.config.ConfigConstants; import org.opends.server.types.HostPort; /** * The object of this class represent an OpenDS server instance. *

* It can represent either a DS-only, a RS-only or a combined DS-RS. */ public class ServerDescriptor { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); private static final String TRUSTSTORE_DN = "cn=ads-truststore"; private final Map adsProperties = new HashMap<>(); private final Set replicas = new HashSet<>(); private final Map serverProperties = new HashMap<>(); private TopologyCacheException lastException; /** * Enumeration containing the different server properties that we can keep in * the ServerProperty object. */ public enum ServerProperty { /** The associated value is a String. */ HOST_NAME(ADSContext.ServerProperty.HOST_NAME), /** The associated value is an List of Integer. */ LDAP_PORT(ADSContext.ServerProperty.LDAP_PORT), /** The associated value is an List of Integer. */ LDAPS_PORT(ADSContext.ServerProperty.LDAPS_PORT), /** The associated value is an Integer. */ ADMIN_PORT(ADSContext.ServerProperty.ADMIN_PORT), /** The associated value is an List of Boolean. */ LDAP_ENABLED(ADSContext.ServerProperty.LDAP_ENABLED), /** The associated value is an List of Boolean. */ LDAPS_ENABLED(ADSContext.ServerProperty.LDAPS_ENABLED), /** The associated value is an List of Boolean. */ ADMIN_ENABLED(ADSContext.ServerProperty.ADMIN_ENABLED), /** The associated value is an List of Boolean. */ STARTTLS_ENABLED(ADSContext.ServerProperty.STARTTLS_ENABLED), /** The associated value is an List of Integer. */ JMX_PORT(ADSContext.ServerProperty.JMX_PORT), /** The associated value is an List of Integer. */ JMXS_PORT(ADSContext.ServerProperty.JMXS_PORT), /** The associated value is an List of Boolean. */ JMX_ENABLED(ADSContext.ServerProperty.JMX_ENABLED), /** The associated value is an List of Boolean. */ JMXS_ENABLED(ADSContext.ServerProperty.JMXS_ENABLED), /** The associated value is an Integer. */ REPLICATION_SERVER_PORT(null), /** The associated value is a Boolean. */ IS_REPLICATION_SERVER(null), /** The associated value is a Boolean. */ IS_REPLICATION_ENABLED(null), /** The associated value is a Boolean. */ IS_REPLICATION_SECURE(null), /** List of servers specified in the Replication Server configuration. This is a Set of String. */ EXTERNAL_REPLICATION_SERVERS(null), /** The associated value is an Integer. */ REPLICATION_SERVER_ID(null), /** * The instance key-pair public-key certificate. The associated value is a * byte[] (ds-cfg-public-key-certificate;binary). */ INSTANCE_PUBLIC_KEY_CERTIFICATE(ADSContext.ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE), /** The schema generation ID. */ SCHEMA_GENERATION_ID(null); private org.opends.admin.ads.ADSContext.ServerProperty adsEquivalent; private ServerProperty(ADSContext.ServerProperty adsEquivalent) { this.adsEquivalent = adsEquivalent; } } /** Default constructor. */ protected ServerDescriptor() { } /** * Returns the replicas contained on the server. * @return the replicas contained on the server. */ public Set getReplicas() { return new HashSet<>(replicas); } /** * Sets the replicas contained on the server. * @param replicas the replicas contained on the server. */ public void setReplicas(Set replicas) { this.replicas.clear(); this.replicas.addAll(replicas); } /** * Returns a Map containing the ADS properties of the server. * @return a Map containing the ADS properties of the server. */ public Map getAdsProperties() { return adsProperties; } /** * Returns a Map containing the properties of the server. * @return a Map containing the properties of the server. */ public Map getServerProperties() { return serverProperties; } /** * Tells whether this server is registered in the ADS. * @return {@code true} if the server is registered in the ADS and {@code false} otherwise. */ public boolean isRegistered() { return !adsProperties.isEmpty(); } /** * Tells whether this server is a replication server. * @return {@code true} if the server is a replication server and {@code false} otherwise. */ public boolean isReplicationServer() { return Boolean.TRUE.equals( serverProperties.get(ServerProperty.IS_REPLICATION_SERVER)); } /** * Tells whether replication is enabled on this server. * @return {@code true} if replication is enabled and {@code false} otherwise. */ public boolean isReplicationEnabled() { return Boolean.TRUE.equals(serverProperties.get(ServerProperty.IS_REPLICATION_ENABLED)); } /** * Returns the String representation of this replication server based * on the information we have ("hostname":"replication port") and * {@code null} if this is not a replication server. * @return the String representation of this replication server based * on the information we have ("hostname":"replication port") and * {@code null} if this is not a replication server. */ public String getReplicationServerHostPort() { return isReplicationServer() ? getReplicationServer(getHostName(), getReplicationServerPort()) : null; } /** * Returns the replication server ID of this server and -1 if this is not a * replications server. * @return the replication server ID of this server and -1 if this is not a * replications server. */ public int getReplicationServerId() { return isReplicationServer() ? (Integer) serverProperties.get(ServerProperty.REPLICATION_SERVER_ID) : -1; } /** * Returns the replication port of this server and -1 if this is not a * replications server. * @return the replication port of this server and -1 if this is not a * replications server. */ public int getReplicationServerPort() { return isReplicationServer() ? (Integer) serverProperties.get(ServerProperty.REPLICATION_SERVER_PORT) : -1; } /** * Returns whether the communication with the replication port on the server is encrypted. * @return {@code true} if the communication with the replication port on * the server is encrypted and {@code false} otherwise. */ public boolean isReplicationSecure() { return isReplicationServer() && Boolean.TRUE.equals(serverProperties.get(ServerProperty.IS_REPLICATION_SECURE)); } /** * Sets the ADS properties of the server. * @param adsProperties a Map containing the ADS properties of the server. */ public void setAdsProperties( Map adsProperties) { this.adsProperties.clear(); this.adsProperties.putAll(adsProperties); } /** * Returns the host name of the server. * @return the host name of the server. */ public String getHostName() { String host = (String)serverProperties.get(ServerProperty.HOST_NAME); if (host != null) { return host; } return (String) adsProperties.get(ADSContext.ServerProperty.HOST_NAME); } /** * Returns the URL to access this server using LDAP. * @return the URL to access this server using LDAP, * {@code null} if the server is not configured to listen on an LDAP port. */ public String getLDAPURL() { return getLDAPUrl0(ServerProperty.LDAP_ENABLED, ServerProperty.LDAP_PORT, false); } /** * Returns the URL to access this server using LDAPS. * @return the URL to access this server using LDAP, * {@code null} if the server is not configured to listen on an LDAPS port. */ public String getLDAPsURL() { return getLDAPUrl0(ServerProperty.LDAPS_ENABLED, ServerProperty.LDAPS_PORT, true); } private String getLDAPUrl0(ServerProperty enabledProp, ServerProperty portProp, boolean useSSL) { int port = getPort(enabledProp, portProp); if (port != -1) { String host = getHostName(); return getLDAPUrl(host, port, useSSL); } return null; } private int getPort(ServerProperty enabledProp, ServerProperty portProp) { if (!serverProperties.isEmpty()) { return getPort(enabledProp, portProp, -1); } return -1; } /** * Returns the URL to access this server using the administration connector. * @return the URL to access this server using the administration connector, * {@code null} if the server cannot get the administration connector. */ public String getAdminConnectorURL() { return getLDAPUrl0(ServerProperty.ADMIN_ENABLED, ServerProperty.ADMIN_PORT, true); } /** * Returns the list of enabled administration ports. * @return the list of enabled administration ports. */ public List getEnabledAdministrationPorts() { List ports = new ArrayList<>(1); List s = (List) serverProperties.get(ServerProperty.ADMIN_ENABLED); List p = (List) serverProperties.get(ServerProperty.ADMIN_PORT); if (s != null) { for (int i=0; i enabledAttrs = new ArrayList<>(); if (securePreferred) { enabledAttrs.add(ADSContext.ServerProperty.ADMIN_ENABLED); enabledAttrs.add(ADSContext.ServerProperty.LDAPS_ENABLED); enabledAttrs.add(ADSContext.ServerProperty.LDAP_ENABLED); } else { enabledAttrs.add(ADSContext.ServerProperty.LDAP_ENABLED); enabledAttrs.add(ADSContext.ServerProperty.ADMIN_ENABLED); enabledAttrs.add(ADSContext.ServerProperty.LDAPS_ENABLED); } for (ADSContext.ServerProperty prop : enabledAttrs) { Object v = adsProperties.get(prop); if (v != null && "true".equalsIgnoreCase(String.valueOf(v))) { ADSContext.ServerProperty portProp = getPortProperty(prop); Object p = adsProperties.get(portProp); if (p != null) { try { port = Integer.parseInt(String.valueOf(p)); } catch (Throwable t) { logger.warn(LocalizableMessage.raw("Error calculating host port: "+t+" in "+ adsProperties, t)); } break; } else { logger.warn(LocalizableMessage.raw("Value for "+portProp+" is null in "+ adsProperties)); } } } } return new HostPort(getHostName(), port); } private int getLdapPort(int port) { return getPort(ServerProperty.LDAP_ENABLED, ServerProperty.LDAP_PORT, port); } private int getLdapsPort(int port) { return getPort(ServerProperty.LDAPS_ENABLED, ServerProperty.LDAPS_PORT, port); } private int getAdminPort(int port) { return getPort(ServerProperty.ADMIN_ENABLED, ServerProperty.ADMIN_PORT, port); } private ADSContext.ServerProperty getPortProperty(ADSContext.ServerProperty prop) { switch (prop) { case ADMIN_ENABLED: return ADSContext.ServerProperty.ADMIN_PORT; case LDAPS_ENABLED: return ADSContext.ServerProperty.LDAPS_PORT; case LDAP_ENABLED: return ADSContext.ServerProperty.LDAP_PORT; default: throw new IllegalStateException("Unexpected prop: "+prop); } } private int getPort(ServerProperty enabledProp, ServerProperty portProp, int defaultValue) { List s = (List) serverProperties.get(enabledProp); if (s != null) { List p = (List) serverProperties.get(portProp); for (int i=0; i s = (List) serverProperties.get(prop); for (Object o : s) { buf.append(":").append(o); } } } else { ADSContext.ServerProperty[] props = { ADSContext.ServerProperty.HOST_NAME, ADSContext.ServerProperty.LDAP_PORT, ADSContext.ServerProperty.LDAPS_PORT, ADSContext.ServerProperty.ADMIN_PORT, ADSContext.ServerProperty.LDAP_ENABLED, ADSContext.ServerProperty.LDAPS_ENABLED, ADSContext.ServerProperty.ADMIN_ENABLED }; for (int i=0; i s = (List) serverProperties.get(sProps[i][0]); List p = (List) serverProperties.get(sProps[i][1]); if (s != null) { int port = getPort(s, p); if (port == -1) { adsProperties.put(adsProps[i][0], "false"); if (!p.isEmpty()) { port = (Integer)p.iterator().next(); } } else { adsProperties.put(adsProps[i][0], "true"); } adsProperties.put(adsProps[i][1], String.valueOf(port)); } } List array = (List) serverProperties.get(ServerProperty.STARTTLS_ENABLED); boolean startTLSEnabled = false; if (array != null && !array.isEmpty()) { startTLSEnabled = Boolean.TRUE.equals(array.get(array.size() -1)); } adsProperties.put(ADSContext.ServerProperty.STARTTLS_ENABLED, Boolean.toString(startTLSEnabled)); adsProperties.put(ADSContext.ServerProperty.ID, getHostPort(true).toString()); adsProperties.put(ADSContext.ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE, getInstancePublicKeyCertificate()); } private int getPort(List enabled, List port) { for (int j = 0; j < enabled.size(); j++) { if (Boolean.TRUE.equals(enabled.get(j))) { return (Integer) port.get(j); } } return -1; } /** * Creates a ServerDescriptor object based on some ADS properties provided. * @param adsProperties the ADS properties of the server. * @return a ServerDescriptor object that corresponds to the provided ADS * properties. */ public static ServerDescriptor createStandalone( Map adsProperties) { ServerDescriptor desc = new ServerDescriptor(); desc.setAdsProperties(adsProperties); return desc; } /** * Creates a ServerDescriptor object based on the configuration that we read * using the provided connection. * @param conn the connection that will be used to read the configuration of the server. * @param filter the topology cache filter describing the information that * must be retrieved. * @return a ServerDescriptor object that corresponds to the read configuration. * @throws IOException if a problem occurred reading the server configuration. */ public static ServerDescriptor createStandalone(ConnectionWrapper conn, TopologyCacheFilter filter) throws IOException { ServerDescriptor desc = new ServerDescriptor(); updateLdapConfiguration(desc, conn); updateAdminConnectorConfiguration(desc, conn); updateJmxConfiguration(desc, conn); updateReplicas(desc, conn, filter); updateReplication(desc, conn, filter); updatePublicKeyCertificate(desc, conn); updateMiscellaneous(desc, conn); desc.serverProperties.put(ServerProperty.HOST_NAME, conn.getHostPort().getHost()); return desc; } private static void updateLdapConfiguration(ServerDescriptor desc, ConnectionWrapper conn) throws IOException { String filter = "(objectclass=ds-cfg-ldap-connection-handler)"; SearchRequest request = newSearchRequest("cn=config", WHOLE_SUBTREE, filter, "ds-cfg-enabled", "ds-cfg-listen-address", "ds-cfg-listen-port", "ds-cfg-use-ssl", "ds-cfg-allow-start-tls", "objectclass"); try (ConnectionEntryReader entryReader = conn.getConnection().search(request)) { List ldapPorts = new ArrayList<>(); List ldapsPorts = new ArrayList<>(); List ldapEnabled = new ArrayList<>(); List ldapsEnabled = new ArrayList<>(); List startTLSEnabled = new ArrayList<>(); desc.serverProperties.put(ServerProperty.LDAP_PORT, ldapPorts); desc.serverProperties.put(ServerProperty.LDAPS_PORT, ldapsPorts); desc.serverProperties.put(ServerProperty.LDAP_ENABLED, ldapEnabled); desc.serverProperties.put(ServerProperty.LDAPS_ENABLED, ldapsEnabled); desc.serverProperties.put(ServerProperty.STARTTLS_ENABLED, startTLSEnabled); while (entryReader.hasNext()) { SearchResultEntry sr = entryReader.readEntry(); Integer portNumber = asInteger(sr, "ds-cfg-listen-port"); boolean enabled = asBoolean(sr, "ds-cfg-enabled"); if (asBoolean(sr, "ds-cfg-use-ssl")) { ldapsPorts.add(portNumber); ldapsEnabled.add(enabled); } else { ldapPorts.add(portNumber); ldapEnabled.add(enabled); startTLSEnabled.add(asBoolean(sr, "ds-cfg-allow-start-tls")); } } } } private static void updateAdminConnectorConfiguration(ServerDescriptor desc, ConnectionWrapper conn) throws IOException { SearchRequest request = newSearchRequest( "cn=config", WHOLE_SUBTREE, "(objectclass=ds-cfg-administration-connector)", "ds-cfg-listen-port", "objectclass"); SearchResultEntry sr = conn.getConnection().searchSingleEntry(request); Integer adminConnectorPort = asInteger(sr, "ds-cfg-listen-port"); // Even if we have a single port, use an array to be consistent with // other protocols. List adminPorts = new ArrayList<>(); List adminEnabled = new ArrayList<>(); if (adminConnectorPort != null) { adminPorts.add(adminConnectorPort); adminEnabled.add(Boolean.TRUE); } desc.serverProperties.put(ServerProperty.ADMIN_PORT, adminPorts); desc.serverProperties.put(ServerProperty.ADMIN_ENABLED, adminEnabled); } private static void updateJmxConfiguration(ServerDescriptor desc, ConnectionWrapper conn) throws IOException { List jmxPorts = new ArrayList<>(); List jmxsPorts = new ArrayList<>(); List jmxEnabled = new ArrayList<>(); List jmxsEnabled = new ArrayList<>(); desc.serverProperties.put(ServerProperty.JMX_PORT, jmxPorts); desc.serverProperties.put(ServerProperty.JMXS_PORT, jmxsPorts); desc.serverProperties.put(ServerProperty.JMX_ENABLED, jmxEnabled); desc.serverProperties.put(ServerProperty.JMXS_ENABLED, jmxsEnabled); String filter = "(objectclass=ds-cfg-jmx-connection-handler)"; SearchRequest request = newSearchRequest("cn=config", WHOLE_SUBTREE, filter, "ds-cfg-enabled", "ds-cfg-listen-address", "ds-cfg-listen-port", "ds-cfg-use-ssl", "objectclass"); try (ConnectionEntryReader entryReader = conn.getConnection().search(request)) { while (entryReader.hasNext()) { SearchResultEntry sr = entryReader.readEntry(); Integer portNumber = asInteger(sr, "ds-cfg-listen-port"); boolean enabled = asBoolean(sr, "ds-cfg-enabled"); if (asBoolean(sr, "ds-cfg-use-ssl")) { jmxsPorts.add(portNumber); jmxsEnabled.add(enabled); } else { jmxPorts.add(portNumber); jmxEnabled.add(enabled); } } } } private static void updateReplicas(ServerDescriptor desc, ConnectionWrapper conn, TopologyCacheFilter cacheFilter) throws IOException { if (!cacheFilter.searchBaseDNInformation()) { return; } SearchRequest request = newSearchRequest("cn=config", WHOLE_SUBTREE, "(objectclass=ds-cfg-backend)", "ds-cfg-base-dn", "ds-cfg-backend-id", ConfigConstants.ATTR_OBJECTCLASS); try (ConnectionEntryReader entryReader = conn.getConnection().search(request)) { while (entryReader.hasNext()) { SearchResultEntry sr = entryReader.readEntry(); String backendId = firstValueAsString(sr, "ds-cfg-backend-id"); if (!isConfigBackend(backendId) || isSchemaBackend(backendId)) { Set entries; if (cacheFilter.searchMonitoringInformation()) { entries = getBaseDNEntryCount(conn, backendId); } else { entries = new HashSet<>(); } Set replicas = desc.getReplicas(); Set baseDns = asSetOfDN(sr, "ds-cfg-base-dn"); for (DN baseDn : baseDns) { if (isAddReplica(cacheFilter, baseDn)) { ReplicaDescriptor replica = new ReplicaDescriptor(); replica.setServer(desc); replica.setObjectClasses(asSetOfString(sr, ConfigConstants.ATTR_OBJECTCLASS)); replica.setBackendId(backendId); replica.setSuffix(new SuffixDescriptor(baseDn, replica)); replica.setEntries(getNumberOfEntriesForBaseDn(entries, baseDn)); replicas.add(replica); } } desc.setReplicas(replicas); } } } } private static int getNumberOfEntriesForBaseDn(Set entries, DN baseDn) { for (String s : entries) { int index = s.indexOf(" "); if (index != -1) { DN dn = DN.valueOf(s.substring(index + 1)); if (baseDn.equals(dn)) { try { return Integer.parseInt(s.substring(0, index)); } catch (Throwable t) { /* Ignore */ } break; } } } return -1; } private static boolean isAddReplica(TopologyCacheFilter cacheFilter, DN baseDn) { return cacheFilter.searchAllBaseDNs() || cacheFilter.getBaseDNsToSearch().contains(baseDn); } private static void updateReplication(ServerDescriptor desc, ConnectionWrapper conn, TopologyCacheFilter cacheFilter) throws IOException { SearchRequest request = newSearchRequest( "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config", WHOLE_SUBTREE, "(objectclass=ds-cfg-synchronization-provider)", "ds-cfg-enabled"); SearchResultEntry sre = conn.getConnection().searchSingleEntry(request); Boolean replicationEnabled = asBoolean(sre, "ds-cfg-enabled"); desc.serverProperties.put(ServerProperty.IS_REPLICATION_ENABLED, replicationEnabled); Set allReplicationServers = new LinkedHashSet<>(); if (cacheFilter.searchBaseDNInformation()) { request = newSearchRequest( "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config", WHOLE_SUBTREE, "(objectclass=ds-cfg-replication-domain)", "ds-cfg-base-dn", "ds-cfg-replication-server", "ds-cfg-server-id" ); try (ConnectionEntryReader entryReader = conn.getConnection().search(request)) { while (entryReader.hasNext()) { SearchResultEntry sr = entryReader.readEntry(); int id = asInteger(sr, "ds-cfg-server-id"); Set replicationServers = asSetOfString(sr, "ds-cfg-replication-server"); Set dns = asSetOfDN(sr, "ds-cfg-base-dn"); for (DN dn : dns) { for (ReplicaDescriptor replica : desc.getReplicas()) { if (replica.getSuffix().getDN().equals(dn)) { replica.setReplicationId(id); LinkedHashSet repServers = toLowercase(replicationServers); replica.setReplicationServers(repServers); allReplicationServers.addAll(repServers); } } } } } } desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER, Boolean.FALSE); request = newSearchRequest( "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config", WHOLE_SUBTREE, "(objectclass=ds-cfg-replication-server)", "ds-cfg-replication-port", "ds-cfg-replication-server", "ds-cfg-replication-server-id" ); try (ConnectionEntryReader entryReader = conn.getConnection().search(request)) { while (entryReader.hasNext()) { SearchResultEntry sr = entryReader.readEntry(); desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER, Boolean.TRUE); Integer port = asInteger(sr, "ds-cfg-replication-port"); desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_PORT, port); Integer serverId = asInteger(sr, "ds-cfg-replication-server-id"); desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_ID, serverId); LinkedHashSet repServers = toLowercase(asSetOfString(sr, "ds-cfg-replication-server")); allReplicationServers.addAll(repServers); desc.serverProperties.put(ServerProperty.EXTERNAL_REPLICATION_SERVERS, allReplicationServers); } } Boolean replicationSecure = isReplicationSecure(conn, replicationEnabled); desc.serverProperties.put(ServerProperty.IS_REPLICATION_SECURE, replicationSecure); } /** * Keep the values of the replication servers in lower case to make use of Sets as String simpler. */ private static LinkedHashSet toLowercase(Set values) { LinkedHashSet repServers = new LinkedHashSet<>(); for (String s: values) { repServers.add(s.toLowerCase()); } return repServers; } private static boolean isReplicationSecure(ConnectionWrapper conn, boolean replicationEnabled) throws IOException { if (replicationEnabled) { SearchRequest request = newSearchRequest( "cn=Crypto Manager,cn=config", BASE_OBJECT, "(objectclass=ds-cfg-crypto-manager)", "ds-cfg-ssl-encryption"); try (ConnectionEntryReader entryReader = conn.getConnection().search(request)) { while (entryReader.hasNext()) { SearchResultEntry sr = entryReader.readEntry(); return asBoolean(sr, "ds-cfg-ssl-encryption"); } } } return false; } /** * Updates the instance key public-key certificate value of this context from the local truststore * of the instance bound by this context. Any current value of the certificate is overwritten. The * intent of this method is to retrieve the instance-key public-key certificate when this context * is bound to an instance, and cache it for later use in registering the instance into ADS. * * @param desc * The map to update with the instance key-pair public-key certificate. * @param connWrapper * The connection to the server. * @throws LdapException * if unable to retrieve certificate from bound instance. */ private static void updatePublicKeyCertificate(ServerDescriptor desc, ConnectionWrapper connWrapper) throws LdapException { /* TODO: this DN is declared in some core constants file. Create a constants file for the installer and import it into the core. */ String dn = "ds-cfg-key-id=ads-certificate,cn=ads-truststore"; Connection conn = connWrapper.getConnection(); for (int i = 0; i < 2 ; ++i) { /* If the entry does not exist in the instance's truststore backend, add it (which induces the CryptoManager to create the public-key certificate attribute), then repeat the search. */ try { SearchRequest request = newSearchRequest( dn, BASE_OBJECT, "(objectclass=ds-cfg-instance-key)", "ds-cfg-public-key-certificate;binary"); SearchResultEntry certEntry = conn.searchSingleEntry(request); final Attribute certAttr = certEntry.getAttribute("ds-cfg-public-key-certificate;binary"); if (null != certAttr) { /* attribute ds-cfg-public-key-certificate is a MUST in the schema */ desc.serverProperties.put( ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE, certAttr.firstValue().toByteArray()); } break; } catch (LdapException e) { if (0 != i || e.getResult().getResultCode() != ResultCode.NO_SUCH_OBJECT) { throw e; } // Poke CryptoManager to initialize truststore. Note the special attribute in the request. AddRequest request = newAddRequest(dn) .addAttribute("objectclass", "top", "ds-cfg-self-signed-cert-request"); conn.add(request); } } } private static void updateMiscellaneous(ServerDescriptor desc, ConnectionWrapper conn) throws IOException { String filter = "(|(objectclass=*)(objectclass=ldapsubentry))"; SearchRequest request = newSearchRequest("cn=schema", BASE_OBJECT, filter, "ds-sync-generation-id"); try (ConnectionEntryReader entryReader = conn.getConnection().search(request)) { while (entryReader.hasNext()) { SearchResultEntry sr = entryReader.readEntry(); desc.serverProperties.put(ServerProperty.SCHEMA_GENERATION_ID, firstValueAsString(sr, "ds-sync-generation-id")); } } } /** Seeds the bound instance's local ads-truststore with a set of instance key-pair public key certificates. The result is the instance will trust any instance possessing the private key corresponding to one of the public-key certificates. This trust is necessary at least to initialize replication, which uses the trusted certificate entries in the ads-truststore for server authentication. @param connWrapper The connection to the server. @param keyEntryMap The set of valid (i.e., not tagged as compromised) instance key-pair public-key certificate entries in ADS represented as a map from keyID to public-key certificate (binary). @throws LdapException in case an error occurs while updating the instance's ads-truststore via LDAP. */ public static void seedAdsTrustStore(ConnectionWrapper connWrapper, Map keyEntryMap) throws LdapException { Connection conn = connWrapper.getConnection(); /* TODO: this DN is declared in some core constants file. Create a constants file for the installer and import it into the core. */ for (Map.Entry keyEntry : keyEntryMap.entrySet()){ String instanceKeyId = ADSContext.ServerProperty.INSTANCE_KEY_ID.getAttributeName(); String instancePublicKeyCertificate = ADSContext.ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE.getAttributeName() + ";binary"; String dn = instanceKeyId + "=" + Rdn.escapeValue(keyEntry.getKey()) + "," + TRUSTSTORE_DN; AddRequest request = newAddRequest(dn) .addAttribute("objectclass", "top", "ds-cfg-instance-key") .addAttribute(instanceKeyId, keyEntry.getKey()) .addAttribute(instancePublicKeyCertificate, keyEntry.getValue()); try { conn.add(request); } catch (LdapException e) { if (e.getResult().getResultCode() != ResultCode.ENTRY_ALREADY_EXISTS) { throw e; } conn.delete(dn); conn.add(request); } } } /** * Returns the values of the ds-base-dn-entry count attributes for the given backend monitor entry * using the provided connection. * * @param conn * the connection to use to update the configuration. * @param backendID * the id of the backend. * @return the values of the ds-base-dn-entry count attribute. * @throws IOException * if there was an error. */ private static Set getBaseDNEntryCount(ConnectionWrapper conn, String backendID) throws IOException { LinkedHashSet results = new LinkedHashSet<>(); SearchRequest request = newSearchRequest("cn=monitor", SINGLE_LEVEL, "(ds-backend-id=" + backendID + ")", "ds-base-dn-entry-count"); try (ConnectionEntryReader entryReader = conn.getConnection().search(request)) { while (entryReader.hasNext()) { SearchResultEntry sr = entryReader.readEntry(); results.addAll(asSetOfString(sr, "ds-base-dn-entry-count")); } } return results; } /** * Returns whether the provided backendID corresponds to a configuration backend. * @param backendId the backend ID to analyze * @return {@code true} if the the id corresponds to a configuration * backend and {@code false} otherwise. */ private static boolean isConfigBackend(String backendId) { return "tasks".equalsIgnoreCase(backendId) || "schema".equalsIgnoreCase(backendId) || "config".equalsIgnoreCase(backendId) || "monitor".equalsIgnoreCase(backendId) || "backup".equalsIgnoreCase(backendId) || "ads-truststore".equalsIgnoreCase(backendId); } /** * Returns whether the provided ID corresponds to the schema backend. * @param backendId the backend ID to analyze * @return {@code true} if the the id corresponds to the schema backend * and {@code false} otherwise. */ private static boolean isSchemaBackend(String backendId) { return "schema".equalsIgnoreCase(backendId); } /** * Returns the replication server normalized String for a given host name * and replication port. * @param hostName the host name. * @param replicationPort the replication port. * @return the replication server normalized String for a given host name * and replication port. */ public static String getReplicationServer(String hostName, int replicationPort) { return HostPort.toString(hostName, replicationPort); } /** * Returns a representation of a base DN for a set of servers. * @param baseDN the base DN. * @param servers the servers. * @return a representation of a base DN for a set of servers. */ public static String getSuffixDisplay(DN baseDN, Set servers) { StringBuilder sb = new StringBuilder(); sb.append(baseDN); for (ServerDescriptor server : servers) { sb.append(Constants.LINE_SEPARATOR).append(" "); sb.append(server.getHostPort(true)); } return sb.toString(); } /** * Tells whether the provided server descriptor represents the same server * as this object. * @param server the server to make the comparison. * @return {@code true} if the provided server descriptor represents the same server * as this object, {@code false} otherwise. */ public boolean isSameServer(ServerDescriptor server) { return getId().equals(server.getId()); } @Override public String toString() { final int defaultPort = -1; final int adminPort = getAdminPort(defaultPort); final int ldapPort = getLdapPort(defaultPort); final int ldapsPort = getLdapsPort(defaultPort); final boolean isRs = isReplicationServer(); StringBuilder sb = new StringBuilder(getClass().getSimpleName()); sb.append("(host-name=").append(getHostName()); if (adminPort != defaultPort) { sb.append(", adminPort=").append(adminPort); } if (ldapPort != defaultPort) { sb.append(", ldapPort=").append(ldapPort); } if (ldapsPort != defaultPort) { sb.append(", ldapsPort=").append(ldapsPort); } sb.append(", isReplicationServer=").append(isRs); if (isRs) { sb.append(", replication-server-id=").append(getReplicationServerId()); } appendInconsistencies(sb); sb.append(")"); return sb.toString(); } private void appendInconsistencies(StringBuilder sb) { Map> inconsistencies = new HashMap<>(); for (ServerProperty prop : ServerProperty.values()) { if (prop.adsEquivalent != null) { Object propVal = toScalar(serverProperties.get(prop)); Object propValue = propVal instanceof byte[] ? (byte[]) propVal : toStringValue(propVal); Object adsPropValue = adsProperties.get(prop.adsEquivalent); if (!Objects.equals(propValue, adsPropValue)) { inconsistencies.put(prop, Pair.of(propValue, adsPropValue)); } } } if (!inconsistencies.isEmpty()) { sb.append(", inconsistencies=").append(inconsistencies); } } private Object toScalar(Object propValue) { if (propValue instanceof List) { List propValues = (List) propValue; return !propValues.isEmpty() ? propValues.get(0) : null; } return propValue; } private String toStringValue(Object propValue) { return propValue != null ? propValue.toString() : null; } }