opends/src/ads/org/opends/admin/ads/ADSContext.java
@@ -64,7 +64,6 @@ */ public class ADSContext { /** * Enumeration containing the different server properties that are stored in * the ADS. @@ -78,19 +77,23 @@ /** * The host name of the server. */ HOSTNAME("hostname"), HOST_NAME("hostname"), /** * The LDAP port of the server. */ PORT("ldapport"), LDAP_PORT("ldapport"), /** * The JMX port of the server. */ JMX_PORT("jmxport"), /** * The JMX secure port of the server. */ JMXS_PORT("jmxsport"), /** * The LDAPS port of the server. */ SECURE_PORT("ldapsport"), LDAPS_PORT("ldapsport"), /** * The certificate used by the server. */ @@ -124,6 +127,10 @@ */ JMX_ENABLED("jmxEnabled"), /** * Whether JMX is enabled or not. */ JMXS_ENABLED("jmxsEnabled"), /** * The location of the server. */ LOCATION("location"), @@ -500,10 +507,10 @@ // Build the filter according the properties passed in serverProperties int operandCount = 0; if (serverProperties.containsKey(ServerProperty.HOSTNAME)) if (serverProperties.containsKey(ServerProperty.HOST_NAME)) { filter.append("(cn="); filter.append(serverProperties.get(ServerProperty.HOSTNAME)); filter.append(serverProperties.get(ServerProperty.HOST_NAME)); filter.append("*"); if (serverProperties.containsKey(ServerProperty.INSTANCE_PATH)) { @@ -513,12 +520,12 @@ filter.append(")"); operandCount++; } if (serverProperties.containsKey(ServerProperty.PORT)) if (serverProperties.containsKey(ServerProperty.LDAP_PORT)) { filter.append("("); filter.append(ServerProperty.PORT); filter.append(ServerProperty.LDAP_PORT); filter.append("="); filter.append(serverProperties.get(ServerProperty.PORT)); filter.append(serverProperties.get(ServerProperty.LDAP_PORT)); filter.append(")"); operandCount++; } @@ -738,9 +745,6 @@ /** * Creates the Administration Data in the server. * NOTE: until we use the Administration Framework this does not work. * TODO: remove the installPath parameter once we are integrated in the * Administration Framework. * The call to this method assumes that OpenDS.jar has already been loaded. * So this should not be called by the Java Web Start before being sure that * this jar is loaded. @@ -759,18 +763,25 @@ } /** * TODO: remove this method if we can assume that the server during setup * can be started to do a number of things. * NOTE: this can only be called locally. * The call to this method assumes that OpenDS.jar has already been loaded. * So this should not be called by the Java Web Start before being sure that * this jar is loaded. * @param serverProperties the properties of the server to register. * @param serverProperties the properties of the servers to register. * @param serverGroupProperties the properties of the server groups to * register. * @param administratorProperties the properties of the administrators to * register. * @param installPath the installation path of the server. * @param backendName the backend where we create the administration data. * @throws ADSContextException if something goes wrong. */ public static void createOfflineAdminData( Map<ServerProperty, Object> serverProperties, String installPath, String backendName) Set<Map<ServerProperty, Object>> serverProperties, Set<Map<ServerGroupProperty, Object>> serverGroupProperties, Set<Map<AdministratorProperty, Object>> administratorProperties, String installPath) throws ADSContextException { // Add the administration suffix @@ -802,31 +813,29 @@ lines.add("dn: "+getServerGroupContainerDN()); lines.add("objectclass: extensibleobject"); lines.add(""); LdapName dn = makeDNFromServerProperties(serverProperties); BasicAttributes attrs = makeAttrsFromServerProperties(serverProperties); lines.add("dn: "+dn.toString()); NamingEnumeration<String> ids = attrs.getIDs(); while (ids.hasMoreElements()) for (Map<ServerProperty, Object> props : serverProperties) { String attrID = ids.nextElement(); Attribute attr = attrs.get(attrID); try { NamingEnumeration values = attr.getAll(); while (values.hasMoreElements()) { lines.add(attrID+": "+values.nextElement()); } } catch (NamingException ne) { // This should not happen throw new ADSContextException( ADSContextException.ErrorType.ERROR_UNEXPECTED, ne); } lines.add(""); LdapName dn = makeDNFromServerProperties(props); BasicAttributes attrs = makeAttrsFromServerProperties(props); addToLines(dn, attrs, lines); } for (Map<ServerGroupProperty, Object> props : serverGroupProperties) { lines.add(""); LdapName dn = makeDNFromServerGroupProperties(props); BasicAttributes attrs = makeAttrsFromServerGroupProperties(props); addToLines(dn, attrs, lines); } for (Map<AdministratorProperty, Object> props : administratorProperties) { lines.add(""); LdapName dn = makeDNFromAdministratorProperties(props); BasicAttributes attrs = makeAttrsFromAdministratorProperties(props); addToLines(dn, attrs, lines); } BufferedWriter writer = new BufferedWriter(new FileWriter(ldifFile)); for (String line : lines) @@ -848,7 +857,7 @@ "config.ldif"); argList.add("-n"); argList.add(backendName); argList.add(getBackendName()); argList.add("-t"); argList.add(ldifFile.getAbsolutePath()); argList.add("-S"); @@ -1141,7 +1150,7 @@ * properties. * @throws ADSContextException if something goes wrong. */ private LdapName makeDNFromServerGroupProperties( private static LdapName makeDNFromServerGroupProperties( Map<ServerGroupProperty, Object> serverGroupProperties) throws ADSContextException { @@ -1179,7 +1188,7 @@ * properties. * @throws ADSContextException if something goes wrong. */ private LdapName makeDNFromAdministratorProperties( private static LdapName makeDNFromAdministratorProperties( Map<AdministratorProperty, Object> adminProperties) throws ADSContextException { @@ -1196,7 +1205,7 @@ * @return the attributes for the given administrator properties. * @throws ADSContextException if something goes wrong. */ private BasicAttributes makeAttrsFromAdministratorProperties( private static BasicAttributes makeAttrsFromAdministratorProperties( Map<AdministratorProperty, Object> adminProperties) throws ADSContextException { @@ -1247,7 +1256,7 @@ switch(property) { case HOSTNAME: case HOST_NAME: result = null; break; case INSTANCE_PATH: @@ -1273,7 +1282,7 @@ * @return the attributes for the given server group properties. * @throws ADSContextException if something goes wrong. */ private BasicAttributes makeAttrsFromServerGroupProperties( private static BasicAttributes makeAttrsFromServerGroupProperties( Map<ServerGroupProperty, Object> serverGroupProperties) { BasicAttributes result = new BasicAttributes(); @@ -1299,7 +1308,7 @@ * @return the attribute for a given server group property. * @throws ADSContextException if something goes wrong. */ private Attribute makeAttrFromServerGroupProperty( private static Attribute makeAttrFromServerGroupProperty( ServerGroupProperty property, Object value) { Attribute result; @@ -1511,7 +1520,7 @@ // // Put hostname and ipath // result.put(ServerProperty.HOSTNAME, hostName); result.put(ServerProperty.HOST_NAME, hostName); result.put(ServerProperty.INSTANCE_PATH, ipath); // @@ -1588,7 +1597,7 @@ * Returns the parent entry of the administrator entries. * @return the parent entry of the administrator entries. */ private static String getAdministratorContainerDN() public static String getAdministratorContainerDN() { return "cn=Administrators," + getAdministrationSuffixDN(); } @@ -1612,7 +1621,7 @@ private static String getHostname( Map<ServerProperty, Object> serverProperties) throws ADSContextException { String result = (String)serverProperties.get(ServerProperty.HOSTNAME); String result = (String)serverProperties.get(ServerProperty.HOST_NAME); if (result == null) { throw new ADSContextException( @@ -1657,7 +1666,7 @@ * @return the Administrator UID for the given properties. * @throws ADSContextException if the administrator UID could not be found. */ private String getAdministratorUID( private static String getAdministratorUID( Map<AdministratorProperty, Object> adminProperties) throws ADSContextException { String result = (String)adminProperties.get( @@ -1677,7 +1686,7 @@ * @throws ADSContextException if the administrator password could not be * found. */ private String getAdministratorPassword( private static String getAdministratorPassword( Map<AdministratorProperty, Object> adminProperties) throws ADSContextException { String result = (String)adminProperties.get( @@ -1837,7 +1846,8 @@ private void createAdministrationSuffix() throws ADSContextException { // TODO: use new administration framework. ADSContextHelper helper = new ADSContextHelper(); helper.createAdministrationSuffix(getDirContext(), getBackendName()); } /** @@ -1849,7 +1859,7 @@ private static void createOfflineAdministrationSuffix(String installPath) throws ADSContextException { // TODO: the call to this method assumes // NOTE: the call to this method assumes // that OpenDS.jar has already been loaded. So this should not be called by // the Java Web Start before being sure that this jar is loaded. ArrayList<String> argList = new ArrayList<String>(); @@ -1881,7 +1891,13 @@ */ private void removeAdministrationSuffix() throws ADSContextException { // TODO: use new administration framework ADSContextHelper helper = new ADSContextHelper(); helper.removeAdministrationSuffix(getDirContext(), getBackendName()); } private static String getBackendName() { return "userRoot"; } /** @@ -1914,4 +1930,30 @@ getAdministratorDN("*") + "\");)"; } private static void addToLines(LdapName dn, BasicAttributes attrs, LinkedList<String> lines) throws ADSContextException { lines.add("dn: "+dn.toString()); NamingEnumeration<String> ids = attrs.getIDs(); while (ids.hasMoreElements()) { String attrID = ids.nextElement(); Attribute attr = attrs.get(attrID); try { NamingEnumeration values = attr.getAll(); while (values.hasMoreElements()) { lines.add(attrID+": "+values.nextElement()); } } catch (NamingException ne) { // This should not happen throw new ADSContextException( ADSContextException.ErrorType.ERROR_UNEXPECTED, ne); } } } } opends/src/ads/org/opends/admin/ads/ADSContextHelper.java
New file @@ -0,0 +1,142 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads; import java.util.SortedSet; import java.util.TreeSet; import javax.naming.ldap.InitialLdapContext; import org.opends.server.admin.client.InitialManagedObject; import org.opends.server.admin.client.ManagementContext; import org.opends.server.admin.client.ldap.JNDIDirContextAdaptor; import org.opends.server.admin.client.ldap.LDAPManagementContext; import org.opends.server.admin.std.client.*; import org.opends.server.admin.std.meta.*; import org.opends.server.types.DN; /** * This is the only class in the org.opends.admin.ads package that uses the * classes from OpenDS.jar (in particular the administration client framework * API). Before calling this class OpenDS.jar must be * loaded. The goal is basically to centralize in one single place the * dependencies of this package on OpenDS.jar. This is done in order the * QuickSetup code to be able to use some of the functionalities provided * by the ADSContext classes before OpenDS.jar is downloaded. */ public class ADSContextHelper { /** * Default constructor. */ public ADSContextHelper() { } /** * Removes the administration suffix. * @param ctx the DirContext to be used. * @param backendName the name of the backend where the administration * suffix is stored. * @throws ADSContextException if the administration suffix could not be * removed. */ public void removeAdministrationSuffix(InitialLdapContext ctx, String backendName) throws ADSContextException { try { ManagementContext mCtx = LDAPManagementContext.createFromContext( JNDIDirContextAdaptor.adapt(ctx)); RootCfgClient root = mCtx.getRootConfiguration(); BackendCfgClient backend = root.getBackend(backendName); if (backend != null) { SortedSet<DN> suffixes = backend.getBackendBaseDN(); if (suffixes != null) { if (suffixes.remove( DN.decode(ADSContext.getAdministrationSuffixDN()))) { if (suffixes.size() > 0) { backend.commit(); } else { root.removeBackend(backendName); } } } } } catch (Throwable t) { throw new ADSContextException( ADSContextException.ErrorType.ERROR_UNEXPECTED, t); } } /** * Creates the Administration Suffix. * @param ctx the DirContext to be used. * @param backendName the name of the backend where the administration * suffix is stored. * @throws ADSContextException if the administration suffix could not be * created. */ public void createAdministrationSuffix(InitialLdapContext ctx, String backendName) throws ADSContextException { try { ManagementContext mCtx = LDAPManagementContext.createFromContext( JNDIDirContextAdaptor.adapt(ctx)); RootCfgClient root = mCtx.getRootConfiguration(); BackendCfgClient backend = root.getBackend(backendName); if (backend == null) { BackendCfgDefn provider = BackendCfgDefn.getInstance(); backend = root.createBackend(provider, backendName, new InitialManagedObject<BackendCfgClient>( BackendCfgDefn.getInstance())); } SortedSet<DN> suffixes = backend.getBackendBaseDN(); if (suffixes == null) { suffixes = new TreeSet<DN>(); } suffixes.add(DN.decode(ADSContext.getAdministrationSuffixDN())); backend.commit(); } catch (Throwable t) { throw new ADSContextException( ADSContextException.ErrorType.ERROR_UNEXPECTED, t); } } } opends/src/ads/org/opends/admin/ads/ReplicaDescriptor.java
@@ -27,6 +27,9 @@ package org.opends.admin.ads; import java.util.HashSet; import java.util.Set; /** * The object of this class represent a Replica (i.e. a suffix in a given * server). @@ -36,6 +39,8 @@ private SuffixDescriptor suffix; private int entries = -1; private ServerDescriptor server; private Set<String> replicationServers = new HashSet<String>(); private int replicationId = -1; /** * Returns the number of entries contained in the replica. @@ -47,6 +52,16 @@ } /** * Returns whether this replica is replicated or not. * @return <CODE>true</CODE> if the replica is replicated and * <CODE>false</CODE> otherwise. */ public boolean isReplicated() { return replicationId != -1; } /** * Sets the number of entries contained in the replica. * @param entries the number of entries contained in the replica. */ @@ -97,4 +112,52 @@ { this.suffix = suffix; } /** * Returns a set containing the String representation of the replication * servers that are defined in the replication domain for this replica. * @return a set containing the String representation of the replication * servers that are defined in the replication domain for this replica. */ public Set<String> getReplicationServers() { HashSet<String> copy = new HashSet<String>(); copy.addAll(replicationServers); return copy; } /** * Sets the list of replication servers (in their String representation) that * are defined in the replication domain for this replica. * @param replicationServers the list of replication servers (in their String * representation) that are defined in the replication domain for this * replica. */ public void setReplicationServers(Set<String> replicationServers) { this.replicationServers.clear(); this.replicationServers.addAll(replicationServers); } /** * Returns the replication server id for the replication domain associated * with this replica. * @return the replication server id for the replication domain associated * with this replica. */ public int getReplicationId() { return replicationId; } /** * Sets the replication server id for the replication domain associated * with this replica. * @param replicationId the replication server id for the replication domain * associated with this replica. */ public void setReplicationId(int replicationId) { this.replicationId = replicationId; } } opends/src/ads/org/opends/admin/ads/ServerDescriptor.java
@@ -27,10 +27,26 @@ package org.opends.admin.ads; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.Context; import javax.naming.NameNotFoundException; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapName; import org.opends.admin.ads.util.ConnectionUtils; /** * The object of this class represent an OpenDS server. @@ -40,6 +56,80 @@ private Map<ADSContext.ServerProperty, Object> adsProperties = new HashMap<ADSContext.ServerProperty, Object>(); private Set<ReplicaDescriptor> replicas = new HashSet<ReplicaDescriptor>(); private Map<ServerProperty, Object> serverProperties = new HashMap<ServerProperty, Object>(); 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, /** * The associated value is an ArrayList of Integer. */ LDAP_PORT, /** * The associated value is an ArrayList of Integer. */ LDAPS_PORT, /** * The associated value is an ArrayList of Boolean. */ LDAP_ENABLED, /** * The associated value is an ArrayList of Boolean. */ LDAPS_ENABLED, /** * The associated value is an ArrayList of Integer. */ JMX_PORT, /** * The associated value is an ArrayList of Integer. */ JMXS_PORT, /** * The associated value is an ArrayList of Boolean. */ JMX_ENABLED, /** * The associated value is an ArrayList of Boolean. */ JMXS_ENABLED, /** * The associated value is an Integer. */ REPLICATION_SERVER_PORT, /** * The associated value is a Boolean. */ IS_REPLICATION_SERVER, /** * The associated value is a Boolean. */ IS_REPLICATION_ENABLED, /** * List of servers specified in the Replication Server configuration. * This is a Set of String. */ EXTERNAL_REPLICATION_SERVERS, /** * The associated value is an Integer. */ REPLICATION_SERVER_ID } private static final Logger LOG = Logger.getLogger(ServerDescriptor.class.getName()); private ServerDescriptor() { } /** * Returns the replicas contained on the server. @@ -47,7 +137,9 @@ */ public Set<ReplicaDescriptor> getReplicas() { return replicas; Set<ReplicaDescriptor> copy = new HashSet<ReplicaDescriptor>(); copy.addAll(replicas); return copy; } /** @@ -56,16 +148,26 @@ */ public void setReplicas(Set<ReplicaDescriptor> replicas) { this.replicas = 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<ADSContext.ServerProperty, Object> getAdsProperties() { return adsProperties; } /** * Returns a Map containing the properties of the server. * @return a Map containing the properties of the server. */ public Map<ADSContext.ServerProperty, Object> getAdsProperties() public Map<ServerProperty, Object> getServerProperties() { return adsProperties; return serverProperties; } /** @@ -79,12 +181,581 @@ } /** * Sets the properties of the server. * @param adsProperties a Map containing the properties of the server. * Sets the ADS properties of the server. * @param adsProperties a Map containing the ADS properties of the server. */ public void setAdsProperties( Map<ADSContext.ServerProperty, Object> adsProperties) { this.adsProperties = 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) { host = (String)adsProperties.get(ADSContext.ServerProperty.HOST_NAME); } return host; } /** * Returns a String of type host-name:port-number for the server. If * the provided securePreferred is set to true the port that will be used * (if LDAPS is enabled) will be the LDAPS port. * @param securePreferred whether to try to use the secure port as part * of the returning String or not. * @return a String of type host-name:port-number for the server. */ public String getHostPort(boolean securePreferred) { String host = getHostName(); int port = -1; if (!serverProperties.isEmpty()) { ArrayList s = (ArrayList)serverProperties.get( ServerProperty.LDAP_ENABLED); ArrayList p = (ArrayList)serverProperties.get( ServerProperty.LDAP_PORT); for (int i=0; i<s.size(); i++) { if (Boolean.TRUE.equals(s.get(i))) { port = (Integer)p.get(i); break; } } if (securePreferred) { s = (ArrayList)serverProperties.get( ServerProperty.LDAPS_ENABLED); p = (ArrayList)serverProperties.get(ServerProperty.LDAPS_PORT); for (int i=0; i<s.size(); i++) { if (Boolean.TRUE.equals(s.get(i))) { port = (Integer)p.get(i); break; } } } } else { boolean secure; Object v = adsProperties.get(ADSContext.ServerProperty.LDAPS_ENABLED); secure = securePreferred && "true".equalsIgnoreCase(String.valueOf(v)); try { if (secure) { port = Integer.parseInt((String)adsProperties.get( ADSContext.ServerProperty.LDAPS_PORT)); } else { port = Integer.parseInt((String)adsProperties.get( ADSContext.ServerProperty.LDAP_PORT)); } } catch (Throwable t) { } } return host + "." + port; } /** * Returns an Id that is unique for this server. * @return an Id that is unique for this server. */ public String getId() { StringBuilder buf = new StringBuilder(); if (serverProperties.size() > 0) { buf.append(serverProperties.get(ServerProperty.HOST_NAME)); ServerProperty [] props = { ServerProperty.LDAP_PORT, ServerProperty.LDAPS_PORT, ServerProperty.LDAP_ENABLED, ServerProperty.LDAPS_ENABLED }; for (int i=0; i<props.length; i++) { ArrayList s = (ArrayList)serverProperties.get(props[i]); for (Object o : s) { buf.append(":"+o); } } } else { ADSContext.ServerProperty[] props = { ADSContext.ServerProperty.HOST_NAME, ADSContext.ServerProperty.LDAP_PORT, ADSContext.ServerProperty.LDAPS_PORT, ADSContext.ServerProperty.LDAP_ENABLED, ADSContext.ServerProperty.LDAPS_ENABLED }; for (int i=0; i<props.length; i++) { if (i != 0) { buf.append(":"); } buf.append(adsProperties.get(props[i])); } } return buf.toString(); } /** * Returns the last exception that was encountered reading the configuration * of the server. Returns null if there was no problem loading the * configuration of the server. * @return the last exception that was encountered reading the configuration * of the server. Returns null if there was no problem loading the * configuration of the server. */ public TopologyCacheException getLastException() { return lastException; } /** * Sets the last exception that occurred while reading the configuration of * the server. * @param lastException the last exception that occurred while reading the * configuration of the server. */ public void setLastException(TopologyCacheException lastException) { this.lastException = lastException; } /** * 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<ADSContext.ServerProperty, Object> adsProperties) { ServerDescriptor desc = new ServerDescriptor(); desc.setAdsProperties(adsProperties); return desc; } /** * Creates a ServerDescriptor object based on the configuration that we read * using the provided InitialLdapContext. * @param ctx the InitialLdapContext that will be used to read the * configuration of the server. * @return a ServerDescriptor object that corresponds to the read * configuration. * @throws NamingException if a problem occurred reading the server * configuration. */ public static ServerDescriptor createStandalone(InitialLdapContext ctx) throws NamingException { ServerDescriptor desc = new ServerDescriptor(); updateLdapConfiguration(desc, ctx); updateJmxConfiguration(desc, ctx); updateReplicas(desc, ctx); updateReplication(desc, ctx); String s = (String)ctx.getEnvironment().get(Context.PROVIDER_URL); try { URI ldapURL = new URI(s); desc.serverProperties.put(ServerProperty.HOST_NAME, ldapURL.getHost()); } catch (URISyntaxException use) { // This is really strange. Seems like a bug somewhere. LOG.log(Level.WARNING, "Error parsing ldap URL "+s, use); } return desc; } private static void updateLdapConfiguration(ServerDescriptor desc, InitialLdapContext ctx) throws NamingException { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-connection-handler-enabled", "ds-cfg-listen-address", "ds-cfg-listen-port", "ds-cfg-use-ssl", "objectclass" }); String filter = "(objectclass=ds-cfg-ldap-connection-handler)"; LdapName jndiName = new LdapName("cn=config"); NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); ArrayList<Integer> ldapPorts = new ArrayList<Integer>(); ArrayList<Integer> ldapsPorts = new ArrayList<Integer>(); ArrayList<Boolean> ldapEnabled = new ArrayList<Boolean>(); ArrayList<Boolean> ldapsEnabled = new ArrayList<Boolean>(); 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); while(listeners.hasMore()) { SearchResult sr = (SearchResult)listeners.next(); String port = getFirstValue(sr, "ds-cfg-listen-port"); boolean isSecure = "true".equalsIgnoreCase( getFirstValue(sr, "ds-cfg-use-ssl")); boolean enabled = "true".equalsIgnoreCase( getFirstValue(sr, "ds-cfg-connection-handler-enabled")); if (isSecure) { ldapsPorts.add(new Integer(port)); ldapsEnabled.add(enabled); } else { ldapPorts.add(new Integer(port)); ldapEnabled.add(enabled); } } } private static void updateJmxConfiguration(ServerDescriptor desc, InitialLdapContext ctx) throws NamingException { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-connection-handler-enabled", "ds-cfg-listen-address", "ds-cfg-listen-port", "ds-cfg-use-ssl", "objectclass" }); String filter = "(objectclass=ds-cfg-jmx-connection-handler)"; LdapName jndiName = new LdapName("cn=config"); NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); ArrayList<Integer> jmxPorts = new ArrayList<Integer>(); ArrayList<Integer> jmxsPorts = new ArrayList<Integer>(); ArrayList<Boolean> jmxEnabled = new ArrayList<Boolean>(); ArrayList<Boolean> jmxsEnabled = new ArrayList<Boolean>(); 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); while(listeners.hasMore()) { SearchResult sr = (SearchResult)listeners.next(); String port = getFirstValue(sr, "ds-cfg-listen-port"); boolean isSecure = "true".equalsIgnoreCase( getFirstValue(sr, "ds-cfg-use-ssl")); boolean enabled = "true".equalsIgnoreCase( getFirstValue(sr, "ds-cfg-connection-handler-enabled")); if (isSecure) { jmxsPorts.add(new Integer(port)); jmxsEnabled.add(enabled); } else { jmxPorts.add(new Integer(port)); jmxEnabled.add(enabled); } } } private static void updateReplicas(ServerDescriptor desc, InitialLdapContext ctx) throws NamingException { SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-backend-base-dn", "ds-cfg-backend-id" }); String filter = "(objectclass=ds-cfg-backend)"; LdapName jndiName = new LdapName("cn=config"); NamingEnumeration databases = ctx.search(jndiName, filter, ctls); while(databases.hasMore()) { SearchResult sr = (SearchResult)databases.next(); String id = getFirstValue(sr, "ds-cfg-backend-id"); if (!isConfigBackend(id)) { Set<String> baseDns = getValues(sr, "ds-cfg-backend-base-dn"); int nEntries = getEntryCount(ctx, id); for (String baseDn : baseDns) { SuffixDescriptor suffix = new SuffixDescriptor(); suffix.setDN(baseDn); ReplicaDescriptor replica = new ReplicaDescriptor(); replica.setServer(desc); Set<ReplicaDescriptor> replicas = new HashSet<ReplicaDescriptor>(); replicas.add(replica); suffix.setReplicas(replicas); replica.setSuffix(suffix); desc.setReplicas(replicas); if (baseDns.size() == 1) { replica.setEntries(nEntries); } else { /* Cannot know how many entries correspond to this replica */ replica.setEntries(-1); } } } } } private static void updateReplication(ServerDescriptor desc, InitialLdapContext ctx) throws NamingException { boolean replicationEnabled = false; SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-synchronization-provider-enabled" }); String filter = "(objectclass=ds-cfg-synchronization-provider)"; LdapName jndiName = new LdapName( "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config"); try { NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls); while(syncProviders.hasMore()) { SearchResult sr = (SearchResult)syncProviders.next(); if ("true".equalsIgnoreCase(getFirstValue(sr, "ds-cfg-synchronization-provider-enabled"))) { replicationEnabled = true; } } } catch (NameNotFoundException nse) { } desc.serverProperties.put(ServerProperty.IS_REPLICATION_ENABLED, replicationEnabled ? Boolean.TRUE : Boolean.FALSE); ctls = new SearchControls(); ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-replication-dn", "ds-cfg-replication-server", "ds-cfg-directory-server-id" }); filter = "(objectclass=ds-cfg-replication-domain-config)"; jndiName = new LdapName( "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config"); try { NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls); while(syncProviders.hasMore()) { SearchResult sr = (SearchResult)syncProviders.next(); int id = Integer.parseInt( getFirstValue(sr, "ds-cfg-directory-server-id")); Set<String> replicationServers = getValues(sr, "ds-cfg-replication-server"); Set<String> dns = getValues(sr, "ds-cfg-replication-dn"); for (String dn : dns) { for (ReplicaDescriptor replica : desc.getReplicas()) { if (areDnsEqual(replica.getSuffix().getDN(), dn)) { replica.setReplicationId(id); replica.setReplicationServers(replicationServers); } } } } } catch (NameNotFoundException nse) { } ctls = new SearchControls(); ctls.setSearchScope(SearchControls.OBJECT_SCOPE); ctls.setReturningAttributes( new String[] { "ds-cfg-replication-server-port", "ds-cfg-replication-server", "ds-cfg-replication-server-id" }); filter = "(objectclass=ds-cfg-replication-server-config)"; jndiName = new LdapName("cn=Replication Server,cn=Multimaster "+ "Synchronization,cn=Synchronization Providers,cn=config"); desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER, Boolean.FALSE); try { NamingEnumeration entries = ctx.search(jndiName, filter, ctls); while(entries.hasMore()) { SearchResult sr = (SearchResult)entries.next(); desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER, Boolean.TRUE); String v = getFirstValue(sr, "ds-cfg-replication-server-port"); desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_PORT, Integer.parseInt(v)); v = getFirstValue(sr, "ds-cfg-replication-server-id"); desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_ID, Integer.parseInt(v)); } } catch (NameNotFoundException nse) { } } /** * Returns the number of entries in a given backend using the provided * InitialLdapContext. * @param ctx the InitialLdapContext to use to update the configuration. * @param backenID the id of the backend. * @return the number of entries in the backend. * @throws NamingException if there was an error. */ private static int getEntryCount(InitialLdapContext ctx, String backendID) throws NamingException { int nEntries = -1; String v = null; SearchControls ctls = new SearchControls(); ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE); ctls.setReturningAttributes( new String[] { "ds-backend-entry-count" }); String filter = "(ds-backend-id="+backendID+")"; LdapName jndiName = new LdapName("cn=monitor"); NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); while(listeners.hasMore()) { SearchResult sr = (SearchResult)listeners.next(); v = getFirstValue(sr, "ds-backend-entry-count"); } try { nEntries = Integer.parseInt(v); } catch (Exception ex) { } return nEntries; } /* * The following 2 methods are convenience methods to retrieve String values * from an entry. */ private static String getFirstValue(SearchResult entry, String attrName) throws NamingException { return ConnectionUtils.getFirstValue(entry, attrName); } private static Set<String> getValues(SearchResult entry, String attrName) throws NamingException { return ConnectionUtils.getValues(entry, attrName); } /** * An convenience method to know if the provided ID corresponds to a * configuration backend or not. * @param id the backend ID to analyze * @return <CODE>true</CODE> if the the id corresponds to a configuration * backend and <CODE>false</CODE> otherwise. */ private static boolean isConfigBackend(String id) { return "tasks".equalsIgnoreCase(id) || "schema".equalsIgnoreCase(id) || "config".equalsIgnoreCase(id) || "monitor".equalsIgnoreCase(id) || "backup".equalsIgnoreCase(id); } /** * Returns <CODE>true</CODE> if the the provided strings represent the same * DN and <CODE>false</CODE> otherwise. * @param dn1 the first dn to compare. * @param dn2 the second dn to compare. * @return <CODE>true</CODE> if the the provided strings represent the same * DN and <CODE>false</CODE> otherwise. */ private static boolean areDnsEqual(String dn1, String dn2) { boolean areDnsEqual = false; try { LdapName name1 = new LdapName(dn1); LdapName name2 = new LdapName(dn2); areDnsEqual = name1.equals(name2); } catch (Exception ex) { } return areDnsEqual; } } opends/src/ads/org/opends/admin/ads/SuffixDescriptor.java
@@ -32,7 +32,8 @@ /** * The object of this class represent a topology of replicas across servers * that have the same suffix DN. They may or might not be replicated. * that have the same suffix DN. If there is more than one replica on the * suffix, the contents of the replicas are replicated. */ public class SuffixDescriptor { @@ -64,7 +65,9 @@ */ public Set<ReplicaDescriptor> getReplicas() { return replicas; Set<ReplicaDescriptor> copy = new HashSet<ReplicaDescriptor>(); copy.addAll(replicas); return copy; } /** @@ -74,78 +77,46 @@ */ public void setReplicas(Set<ReplicaDescriptor> replicas) { this.replicas = replicas; } /** * {@inheritDoc} */ public boolean equals(Object v) { // TODO: this is buggy code boolean equals = false; if (this != v) { if (v instanceof SuffixDescriptor) { SuffixDescriptor desc = (SuffixDescriptor)v; equals = getDN().equals(desc.getDN()); if (equals) { equals = getReplicas().size() == desc.getReplicas().size(); } if (equals) { for (ReplicaDescriptor repl : getReplicas()) { boolean serverFound = false; ServerDescriptor server = repl.getServer(); for (ReplicaDescriptor repl1 : desc.getReplicas()) { serverFound = serversEqual(server, repl1.getServer()); if (serverFound) { break; } } if (!serverFound) { equals = false; break; } } } } } else { equals = true; } return equals; this.replicas.clear(); this.replicas.addAll(replicas); } /** * Tells whether the two provided objects represent the same server or not. * @param serv1 the first ServerDescriptor to compare. * @param serv2 the second ServerDescriptor to compare. * @return <CODE>true</CODE> if the two objects represent the same server * and <CODE>false</CODE> otherwise. * Returns the Set of Replication servers for the whole suffix topology. The * servers are provided in their String representation. * @return the Set of Replication servers for the whole suffix topology. */ private boolean serversEqual(ServerDescriptor serv1, ServerDescriptor serv2) public Set<String> getReplicationServers() { return serv1.getAdsProperties().equals(serv2.getAdsProperties()); Set<String> replicationServers = new HashSet<String>(); for (ReplicaDescriptor replica : getReplicas()) { replicationServers.addAll(replica.getReplicationServers()); } return replicationServers; } /** * {@inheritDoc} */ public int hashCode() { // FIXME: this is buggy code return getDN().hashCode(); return getId().hashCode(); } /** * Returns an Id that is unique for this suffix. * @return an Id that is unique for this suffix. */ public String getId() { StringBuilder buf = new StringBuilder(); buf.append(getDN()); for (ReplicaDescriptor replica : getReplicas()) { buf.append("-"+replica.getServer().getId()); } return buf.toString(); } } opends/src/ads/org/opends/admin/ads/TopologyCache.java
New file @@ -0,0 +1,256 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.ldap.LdapName; import org.opends.admin.ads.ADSContext.ServerProperty; import org.opends.admin.ads.util.ApplicationTrustManager; import org.opends.admin.ads.util.ServerLoader; /** * This class allows to read the configuration of the different servers that * are registered in a given ADS server. It provides a read only view of the * configuration of the servers and of the replication topologies that might * be configured between them. */ public class TopologyCache { private ADSContext adsContext; private ApplicationTrustManager trustManager; private String dn; private String pwd; private Set<ServerDescriptor> servers = new HashSet<ServerDescriptor>(); private Set<SuffixDescriptor> suffixes = new HashSet<SuffixDescriptor>(); private final boolean isMultiThreaded = true; private final static int MULTITHREAD_TIMEOUT = 10000; private static final Logger LOG = Logger.getLogger(TopologyCache.class.getName()); /** * Constructor of the TopologyCache. * @param adsContext the adsContext to the ADS registry. * @param trustManager the ApplicationTrustManager that must be used to trust * certificates when we create connections to the registered servers to read * their configuration. * @throws TopologyCacheException if an error occurred reading the * authentication data in the DirContext associated with the provided * ADSContext. */ public TopologyCache(ADSContext adsContext, ApplicationTrustManager trustManager) throws TopologyCacheException { this.adsContext = adsContext; this.trustManager = trustManager; try { dn = (String)adsContext.getDirContext().getEnvironment().get( Context.SECURITY_PRINCIPAL); pwd = (String)adsContext.getDirContext().getEnvironment().get( Context.SECURITY_CREDENTIALS); } catch (NamingException ne) { throw new TopologyCacheException(TopologyCacheException.Type.BUG, ne); } } /** * Reads the configuration of the registered servers. * @throws TopologyCacheException if there is an issue reading the * configuration of the registered servers. */ public void reloadTopology() throws TopologyCacheException { suffixes.clear(); servers.clear(); try { Set<Map<ServerProperty,Object>> adsServers = adsContext.readServerRegistry(); Set<ServerLoader> threadSet = new HashSet<ServerLoader>(); for (Map<ServerProperty,Object> serverProperties : adsServers) { ServerLoader t = getServerLoader(serverProperties); if (isMultiThreaded) { t.start(); threadSet.add(t); } else { t.run(); } } if (isMultiThreaded) { joinThreadSet(threadSet); } /* Try to consolidate things (even if the data is not complete). */ HashMap<LdapName, Set<SuffixDescriptor>> hmSuffixes = new HashMap<LdapName, Set<SuffixDescriptor>>(); for (ServerLoader loader : threadSet) { ServerDescriptor descriptor = loader.getServerDescriptor(); for (ReplicaDescriptor replica : descriptor.getReplicas()) { boolean suffixFound = false; LdapName dn = new LdapName(replica.getSuffix().getDN()); Set<SuffixDescriptor> sufs = hmSuffixes.get(dn); if (sufs != null) { Iterator<SuffixDescriptor> it = sufs.iterator(); while (it.hasNext() && !suffixFound) { SuffixDescriptor suffix = it.next(); Iterator<String> it2 = suffix.getReplicationServers().iterator(); while (it2.hasNext() && !suffixFound) { if (replica.getReplicationServers().contains(it2.next())) { suffixFound = true; Set<ReplicaDescriptor> replicas = suffix.getReplicas(); replicas.add(replica); suffix.setReplicas(replicas); replica.setSuffix(suffix); } } } } if (!suffixFound) { if (sufs == null) { sufs = new HashSet<SuffixDescriptor>(); hmSuffixes.put(dn, sufs); } sufs.add(replica.getSuffix()); suffixes.add(replica.getSuffix()); } } servers.add(descriptor); } } catch (ADSContextException ade) { throw new TopologyCacheException(ade); } catch (Throwable t) { throw new TopologyCacheException(TopologyCacheException.Type.BUG, t); } } /** * Returns a Set containing all the servers that are registered in the ADS. * @return a Set containing all the servers that are registered in the ADS. */ public Set<ServerDescriptor> getServers() { HashSet<ServerDescriptor> copy = new HashSet<ServerDescriptor>(); copy.addAll(servers); return copy; } /** * Returns a Set containing the suffixes (replication topologies) that could * be retrieved after the last call to reloadTopology. * @return a Set containing the suffixes (replication topologies) that could * be retrieved after the last call to reloadTopology. */ public Set<SuffixDescriptor> getSuffixes() { HashSet<SuffixDescriptor> copy = new HashSet<SuffixDescriptor>(); copy.addAll(suffixes); return copy; } /** * Method used to wait at most a certain time (MULTITHREAD_TIMEOUT) for the * different threads to finish. * @param threadSet the list of threads (we assume that they are started) * that we must wait for. */ private void joinThreadSet(Set<ServerLoader> threadSet) { Date startDate = new Date(); for (ServerLoader t : threadSet) { long timeToJoin = MULTITHREAD_TIMEOUT - System.currentTimeMillis() + startDate.getTime(); try { if (timeToJoin > 0) { t.join(MULTITHREAD_TIMEOUT); } } catch (InterruptedException ie) { LOG.log(Level.INFO, ie + " caught and ignored", ie); } if (t.isAlive()) { t.interrupt(); } } Date endDate = new Date(); long workingTime = endDate.getTime() - startDate.getTime(); LOG.log(Level.INFO, "Loading ended at "+ workingTime + " ms"); } /** * Creates a ServerLoader object based on the provided server properties. * @param serverProperties the server properties to be used to generate * the ServerLoader. * @return a ServerLoader object based on the provided server properties. */ private ServerLoader getServerLoader( Map<ServerProperty,Object> serverProperties) { return new ServerLoader(serverProperties, dn, pwd, trustManager.createCopy()); } } opends/src/ads/org/opends/admin/ads/TopologyCacheException.java
New file @@ -0,0 +1,148 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads; import javax.naming.NamingException; import org.opends.admin.ads.util.ApplicationTrustManager; /** * This class represents the Exception that can occur while reading server * configuration through the TopologyCache class. */ public class TopologyCacheException extends Exception { private static final long serialVersionUID = 1709535837273360382L; private Type type; private String ldapUrl; private ApplicationTrustManager trustManager; /** * Error type. */ public enum Type { /** * Error reading the ADS. */ GENERIC_READING_ADS, /** * Creating connection to a particular server. */ GENERIC_CREATING_CONNECTION, /** * Error reading the configuration of a particular server. */ GENERIC_READING_SERVER, /** * The DN provided in the DirContext of ADS is not of a global * administrator. */ NOT_GLOBAL_ADMINISTRATOR, /** * Timeout reading the configuration of a particular server. */ TIMEOUT, /** * Unexpected error. */ BUG } /** * Constructor for the exception that must be generated when an * ADSContextException occurs. * @param ace the exception which is the cause of this exception. */ public TopologyCacheException(ADSContextException ace) { type = Type.GENERIC_READING_ADS; initCause(ace); } /** * Constructor for a generic Exception. * @param type the type of this exception. * @param t the cause of this exception. */ public TopologyCacheException(Type type, Throwable t) { this.type = type; initCause(t); } /** * Constructor for the exception that must be generated when a * NamingException occurs. * @param type the type of this exception. * @param ne the NamingException that generated this exception. * @param trustManager the ApplicationTrustManager used when the * NamingException occurred. * @param ldapUrl the LDAP URL of the server we where connected to (or trying * to connect) when the NamingException was generated. */ public TopologyCacheException(Type type, NamingException ne, ApplicationTrustManager trustManager, String ldapUrl) { this.type = type; initCause(ne); this.ldapUrl = ldapUrl; this.trustManager = trustManager; } /** * Returns the type of this exception. * @return the type of this exception. */ public Type getType() { return type; } /** * Returns the LDAP URL of the server we where connected to (or trying * to connect) when this exception was generated. * @return the LDAP URL of the server we where connected to (or trying * to connect) when this exception was generated. */ public String getLdapUrl() { return ldapUrl; } /** * Returns the ApplicationTrustManager that we were using when this exception * was generated. * @return the ApplicationTrustManager that we were using when this exception * was generated. */ public ApplicationTrustManager getTrustManager() { return trustManager; } } opends/src/ads/org/opends/admin/ads/util/ApplicationTrustManager.java
New file @@ -0,0 +1,402 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads.util; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; /** * This class is in charge of checking whether the certificates that are * presented are trusted or not. * This implementation tries to check also that the subject DN of the * certificate corresponds to the host passed using the setHostName method. * * The constructor tries to use a default TrustManager from the system and if * it cannot be retrieved this class will only accept the certificates * explicitly accepted by the user (and specified by calling acceptCertificate). * * NOTE: this class is not aimed to be used when we have connections in paralel. */ public class ApplicationTrustManager implements X509TrustManager { /** * The enumeration for the different causes for which the trust manager can * refuse to accept a certificate. */ public enum Cause { /** * The certificate was not trusted. */ NOT_TRUSTED, /** * The certificate's subject DN's value and the host name we tried to * connect to do not match. */ HOST_NAME_MISMATCH } static private final Logger LOG = Logger.getLogger(ApplicationTrustManager.class.getName()); private X509TrustManager sunJSSEX509TrustManager; private String lastRefusedAuthType; private X509Certificate[] lastRefusedChain; private Cause lastRefusedCause; /* * The following ArrayList contain information about the certificates * explicitly accepted by the user. */ private ArrayList<X509Certificate[]> acceptedChains = new ArrayList<X509Certificate[]>(); private ArrayList<String> acceptedAuthTypes = new ArrayList<String>(); private ArrayList<String> acceptedHosts = new ArrayList<String>(); private String host; /** * The default constructor. */ public ApplicationTrustManager() { TrustManagerFactory tmf = null; String algo = "SunX509"; String provider = "SunJSSE"; try { tmf = TrustManagerFactory.getInstance(algo, provider); tmf.init((KeyStore)null); sunJSSEX509TrustManager = (X509TrustManager)(tmf.getTrustManagers())[0]; } catch (NoSuchAlgorithmException e) { // Nothing to do: if this occurs we will systematically refuse the // certificates. Maybe we should avoid this and be strict, but we are // in a best effor mode. LOG.log(Level.WARNING, "Error with the algorithm", e); } catch (NoSuchProviderException e) { // Nothing to do: if this occurs we will systematically refuse the // certificates. Maybe we should avoid this and be strict, but we are // in a best effor mode. LOG.log(Level.WARNING, "Error with the provider", e); } catch (KeyStoreException e) { // Nothing to do: if this occurs we will systematically refuse the // certificates. Maybe we should avoid this and be strict, but we are // in a best effor mode. LOG.log(Level.WARNING, "Error with the keystore", e); } } /** * {@inheritDoc} */ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { boolean explicitlyAccepted = false; try { if (sunJSSEX509TrustManager != null) { try { sunJSSEX509TrustManager.checkClientTrusted(chain, authType); } catch (CertificateException ce) { verifyAcceptedCertificates(chain, authType); explicitlyAccepted = true; } } else { verifyAcceptedCertificates(chain, authType); explicitlyAccepted = true; } } catch (CertificateException ce) { lastRefusedChain = chain; lastRefusedAuthType = authType; lastRefusedCause = Cause.NOT_TRUSTED; throw ce; } if (!explicitlyAccepted) { try { verifyHostName(chain, authType); } catch (CertificateException ce) { lastRefusedChain = chain; lastRefusedAuthType = authType; lastRefusedCause = Cause.HOST_NAME_MISMATCH; throw ce; } } } /** * {@inheritDoc} */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { boolean explicitlyAccepted = false; try { if (sunJSSEX509TrustManager != null) { try { sunJSSEX509TrustManager.checkServerTrusted(chain, authType); } catch (CertificateException ce) { verifyAcceptedCertificates(chain, authType); explicitlyAccepted = true; } } else { verifyAcceptedCertificates(chain, authType); explicitlyAccepted = true; } } catch (CertificateException ce) { lastRefusedChain = chain; lastRefusedAuthType = authType; lastRefusedCause = Cause.NOT_TRUSTED; throw ce; } if (!explicitlyAccepted) { try { verifyHostName(chain, authType); } catch (CertificateException ce) { lastRefusedChain = chain; lastRefusedAuthType = authType; lastRefusedCause = Cause.HOST_NAME_MISMATCH; throw ce; } } } /** * {@inheritDoc} */ public X509Certificate[] getAcceptedIssuers() { if (sunJSSEX509TrustManager != null) { return sunJSSEX509TrustManager.getAcceptedIssuers(); } else { return new X509Certificate[0]; } } /** * This method is called when the user accepted a certificate. * @param chain the certificate chain accepted by the user. * @param authType the authentication type. * @param host the host we tried to connect and that presented the * certificate. */ public void acceptCertificate(X509Certificate[] chain, String authType, String host) { acceptedChains.add(chain); acceptedAuthTypes.add(authType); acceptedHosts.add(host); } /** * Sets the host name we are trying to contact in a secure mode. This * method is used if we want to verify the correspondance between the * hostname and the subject DN of the certificate that is being presented. * If this method is never called (or called passing null) no verification * will be made on the host name. * @param host the host name we are trying to contact in a secure mode. */ public void setHost(String host) { this.host = host; } /** * This is a method used to set to null the different members that provide * information about the last refused certificate. It is recommended to * call this method before trying to establish a connection using this * trust manager. */ public void resetLastRefusedItems() { lastRefusedAuthType = null; lastRefusedChain = null; lastRefusedCause = null; } /** * Creates a copy of this ApplicationTrustManager. * @return a copy of this ApplicationTrustManager. */ public ApplicationTrustManager createCopy() { ApplicationTrustManager copy = new ApplicationTrustManager(); copy.lastRefusedAuthType = lastRefusedAuthType; copy.lastRefusedChain = lastRefusedChain; copy.lastRefusedCause = lastRefusedCause; copy.acceptedChains.addAll(acceptedChains); copy.acceptedAuthTypes.addAll(acceptedAuthTypes); copy.acceptedHosts.addAll(acceptedHosts); copy.host = host; return copy; } /** * Verifies whether the provided chain and authType have been already accepted * by the user or not. If they have not a CertificateException is thrown. * @param chain the certificate chain to analyze. * @param authType the authentication type. * @throws CertificateException if the provided certificate chain and the * authentication type have not been accepted explicitly by the user. */ private void verifyAcceptedCertificates(X509Certificate[] chain, String authType) throws CertificateException { boolean found = false; for (int i=0; i<acceptedChains.size() && !found; i++) { if (authType.equals(acceptedAuthTypes.get(i))) { X509Certificate[] current = acceptedChains.get(i); found = current.length == chain.length; for (int j=0; j<chain.length && found; j++) { found = chain[j].equals(current[j]); } } } if (!found) { throw new CertificateException( "Certificate not in list of accepted certificates"); } } /** * Verifies that the provided certificate chains subject DN corresponds to the * host name specified with the setHost method. * @param chain the certificate chain to analyze. * @throws CertificateException if the subject DN of the certificate does * not match with the host name specified with the method setHost. */ private void verifyHostName(X509Certificate[] chain, String authType) throws CertificateException { if (host != null) { boolean matches = false; try { LdapName dn = new LdapName(chain[0].getSubjectX500Principal().getName()); Rdn rdn = dn.getRdn(0); String value = rdn.getValue().toString(); matches = host.equalsIgnoreCase(value); } catch (Throwable t) { LOG.log(Level.WARNING, "Error parsing subject dn: "+ chain[0].getSubjectX500Principal(), t); } if (!matches) { throw new CertificateException("Hostname mismatch between host name "+ host+" and subject DN: "+chain[0].getSubjectX500Principal()); } } } /** * Returns the authentication type for the last refused certificate. * @return the authentication type for the last refused certificate. */ public String getLastRefusedAuthType() { return lastRefusedAuthType; } /** * Returns the last cause for refusal of a certificate. * @return the last cause for refusal of a certificate. */ public Cause getLastRefusedCause() { return lastRefusedCause; } /** * Returns the certificate chain for the last refused certificate. * @return the certificate chain for the last refused certificate. */ public X509Certificate[] getLastRefusedChain() { return lastRefusedChain; } } opends/src/ads/org/opends/admin/ads/util/BlindHostnameVerifier.java
New file @@ -0,0 +1,45 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads.util; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; /** * A HostnameVerifier which verifies nothing. */ class BlindHostnameVerifier implements HostnameVerifier { /** * {@inheritDoc} */ public boolean verify(String hostname, SSLSession session) { return true; } } opends/src/ads/org/opends/admin/ads/util/BlindTrustManager.java
New file @@ -0,0 +1,63 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads.util; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; /** * An X509TrustManager which trusts everything. */ class BlindTrustManager implements X509TrustManager { /** * {@inheritDoc} */ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { } /** * {@inheritDoc} */ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { } /** * {@inheritDoc} */ public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } } opends/src/ads/org/opends/admin/ads/util/ConnectionUtils.java
New file @@ -0,0 +1,548 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads.util; import java.io.IOException; import java.net.ConnectException; import java.util.HashSet; import java.util.Hashtable; import java.util.Set; import javax.naming.CommunicationException; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.StartTlsRequest; import javax.naming.ldap.StartTlsResponse; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.TrustManager; /** * Class providing some utilities to create LDAP connections using JNDI and * to manage entries retrieved using JNDI. * */ public class ConnectionUtils { private static final int DEFAULT_LDAP_CONNECT_TIMEOUT = 10000; /** * Private constructor: this class cannot be instantiated. */ private ConnectionUtils() { } /** * Creates a clear LDAP connection and returns the corresponding LdapContext. * This methods uses the specified parameters to create a JNDI environment * hashtable and creates an InitialLdapContext instance. * * @param ldapURL * the target LDAP URL * @param dn * passed as Context.SECURITY_PRINCIPAL if not null * @param pwd * passed as Context.SECURITY_CREDENTIALS if not null * @param timeout * passed as com.sun.jndi.ldap.connect.timeout if > 0 * @param env * null or additional environment properties * * @throws NamingException * the exception thrown when instantiating InitialLdapContext * * @return the created InitialLdapContext. * @see javax.naming.Context * @see javax.naming.ldap.InitialLdapContext */ public static InitialLdapContext createLdapContext(String ldapURL, String dn, String pwd, int timeout, Hashtable<String, String> env) throws NamingException { if (env != null) { // We clone 'env' so that we can modify it freely env = new Hashtable<String, String>(env); } else { env = new Hashtable<String, String>(); } env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, ldapURL); if (timeout >= 1) { env.put("com.sun.jndi.ldap.connect.timeout", String.valueOf(timeout)); } if (dn != null) { env.put(Context.SECURITY_PRINCIPAL, dn); } if (pwd != null) { env.put(Context.SECURITY_CREDENTIALS, pwd); } /* Contains the DirContext and the Exception if any */ final Object[] pair = new Object[] { null, null }; final Hashtable fEnv = env; Thread t = new Thread(new Runnable() { public void run() { try { pair[0] = new InitialLdapContext(fEnv, null); } catch (NamingException ne) { pair[1] = ne; } catch (Throwable t) { pair[1] = t; } } }); return getInitialLdapContext(t, pair, timeout); } /** * Creates an LDAPS connection and returns the corresponding LdapContext. * This method uses the TrusteSocketFactory class so that the specified * trust manager gets called during the SSL handshake. If trust manager is * null, certificates are not verified during SSL handshake. * * @param ldapsURL the target *LDAPS* URL. * @param dn passed as Context.SECURITY_PRINCIPAL if not null. * @param pwd passed as Context.SECURITY_CREDENTIALS if not null. * @param timeout passed as com.sun.jndi.ldap.connect.timeout if > 0. * @param env null or additional environment properties. * @param trustManager null or the trust manager to be invoked during SSL * negociation. * * @return the established connection with the given parameters. * * @throws NamingException the exception thrown when instantiating * InitialLdapContext. * * @see javax.naming.Context * @see javax.naming.ldap.InitialLdapContext * @see TrustedSocketFactory */ public static InitialLdapContext createLdapsContext(String ldapsURL, String dn, String pwd, int timeout, Hashtable<String, String> env, TrustManager trustManager) throws NamingException { if (env != null) { // We clone 'env' so that we can modify it freely env = new Hashtable<String, String>(env); } else { env = new Hashtable<String, String>(); } env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, ldapsURL); env.put("java.naming.ldap.factory.socket", org.opends.admin.ads.util.TrustedSocketFactory.class.getName()); if (dn != null) { env.put(Context.SECURITY_PRINCIPAL, dn); } if (pwd != null) { env.put(Context.SECURITY_CREDENTIALS, pwd); } if (trustManager == null) { trustManager = new BlindTrustManager(); } /* Contains the DirContext and the Exception if any */ final Object[] pair = new Object[] {null, null}; final Hashtable fEnv = env; final TrustManager fTrustManager = trustManager; Thread t = new Thread(new Runnable() { public void run() { try { TrustedSocketFactory.setCurrentThreadTrustManager(fTrustManager); pair[0] = new InitialLdapContext(fEnv, null); } catch (NamingException ne) { pair[1] = ne; } catch (RuntimeException re) { pair[1] = re; } } }); return getInitialLdapContext(t, pair, timeout); } /** * Creates an LDAP+StartTLS connection and returns the corresponding * LdapContext. * This method first creates an LdapContext with anonymous bind. Then it * requests a StartTlsRequest extended operation. The StartTlsResponse is * setup with the specified hostname verifier. Negotiation is done using a * TrustSocketFactory so that the specified TrustManager gets called during * the SSL handshake. * If trust manager is null, certificates are not checked during SSL * handshake. * * @param ldapsURL the target *LDAPS* URL. * @param dn passed as Context.SECURITY_PRINCIPAL if not null. * @param pwd passed as Context.SECURITY_CREDENTIALS if not null. * @param timeout passed as com.sun.jndi.ldap.connect.timeout if > 0. * @param env null or additional environment properties. * @param trustManager null or the trust manager to be invoked during SSL. * negociation. * @param verifier null or the hostname verifier to be setup in the * StartTlsResponse. * * @return the established connection with the given parameters. * * @throws NamingException the exception thrown when instantiating * InitialLdapContext. * * @see javax.naming.Context * @see javax.naming.ldap.InitialLdapContext * @see javax.naming.ldap.StartTlsRequest * @see javax.naming.ldap.StartTlsResponse * @see TrustedSocketFactory */ public static InitialLdapContext createStartTLSContext(String ldapsURL, String dn, String pwd, int timeout, Hashtable<String, String> env, TrustManager trustManager, HostnameVerifier verifier) throws NamingException { if (trustManager == null) { trustManager = new BlindTrustManager(); } if (verifier == null) { verifier = new BlindHostnameVerifier(); } if (env != null) { // We clone 'env' to modify it freely env = new Hashtable<String, String>(env); } else { env = new Hashtable<String, String>(); } env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, ldapsURL); env.put(Context.SECURITY_AUTHENTICATION , "none"); /* Contains the DirContext and the Exception if any */ final Object[] pair = new Object[] {null, null}; final Hashtable fEnv = env; final String fDn = dn; final String fPwd = pwd; final TrustManager fTrustManager = trustManager; final HostnameVerifier fVerifier = verifier; Thread t = new Thread(new Runnable() { public void run() { try { StartTlsResponse tls; InitialLdapContext result = new InitialLdapContext(fEnv, null); tls = (StartTlsResponse) result.extendedOperation( new StartTlsRequest()); tls.setHostnameVerifier(fVerifier); try { tls.negotiate(new TrustedSocketFactory(fTrustManager)); } catch(IOException x) { NamingException xx; xx = new CommunicationException( "Failed to negotiate Start TLS operation"); xx.initCause(x); result.close(); throw xx; } if (fDn != null) { result.addToEnvironment(Context.SECURITY_AUTHENTICATION , "simple"); result.addToEnvironment(Context.SECURITY_PRINCIPAL, fDn); if (fPwd != null) { result.addToEnvironment(Context.SECURITY_CREDENTIALS, fPwd); } result.reconnect(null); } pair[0] = result; } catch (NamingException ne) { pair[1] = ne; } catch (RuntimeException re) { pair[1] = re; } } }); return getInitialLdapContext(t, pair, timeout); } /** * Method used to know if we can connect as administrator in a server with a * given password and dn. * @param ldapUrl the ldap URL of the server. * @param dn the dn to be used. * @param pwd the password to be used. * @return <CODE>true</CODE> if we can connect and read the configuration and * <CODE>false</CODE> otherwise. */ public static boolean canConnectAsAdministrativeUser(String ldapUrl, String dn, String pwd) { boolean canConnectAsAdministrativeUser = false; try { InitialLdapContext ctx = createLdapContext(ldapUrl, dn, pwd, getDefaultLDAPTimeout(), null); /* * Search for the config to check that it is the directory manager. */ SearchControls searchControls = new SearchControls(); searchControls.setCountLimit(1); searchControls.setSearchScope( SearchControls. OBJECT_SCOPE); searchControls.setReturningAttributes( new String[] {"dn"}); ctx.search("cn=config", "objectclass=*", searchControls); canConnectAsAdministrativeUser = true; } catch (NamingException ne) { // Nothing to do. } catch (Throwable t) { throw new IllegalStateException("Unexpected throwable.", t); } return canConnectAsAdministrativeUser; }/** * This is just a commodity method used to try to get an InitialLdapContext. * @param t the Thread to be used to create the InitialLdapContext. * @param pair an Object[] array that contains the InitialLdapContext and the * Throwable if any occurred. * @param timeout the timeout. If we do not get to create the connection * before the timeout a CommunicationException will be thrown. * @return the created InitialLdapContext * @throws NamingException if something goes wrong during the creation. */ private static InitialLdapContext getInitialLdapContext(Thread t, Object[] pair, int timeout) throws NamingException { try { if (timeout > 0) { t.start(); t.join(timeout); } else { t.run(); } } catch (InterruptedException x) { // This might happen for problems in sockets // so it does not necessarily imply a bug } boolean throwException = false; if ((timeout > 0) && t.isAlive()) { t.interrupt(); try { t.join(2000); } catch (InterruptedException x) { // This might happen for problems in sockets // so it does not necessarily imply a bug } throwException = true; } if ((pair[0] == null) && (pair[1] == null)) { throwException = true; } if (throwException) { NamingException xx; ConnectException x = new ConnectException("Connection timed out"); xx = new CommunicationException("Connection timed out"); xx.initCause(x); throw xx; } if (pair[1] != null) { if (pair[1] instanceof NamingException) { throw (NamingException) pair[1]; } else if (pair[1] instanceof RuntimeException) { throw (RuntimeException) pair[1]; } else if (pair[1] instanceof Throwable) { throw new IllegalStateException("Unexpected throwable occurred", (Throwable) pair[1]); } } return (InitialLdapContext) pair[0]; } /** * Returns the default LDAP timeout in milliseconds when we try to connect to * a server. * @return the default LDAP timeout in milliseconds when we try to connect to * a server. */ public static int getDefaultLDAPTimeout() { return DEFAULT_LDAP_CONNECT_TIMEOUT; } /** * Returns the String that can be used to represent a given host name in a * LDAP URL. * This method must be used when we have IPv6 addresses (the address in the * LDAP URL must be enclosed with brackets). * @param host the host name. * @return the String that can be used to represent a given host name in a * LDAP URL. */ public static String getHostNameForLdapUrl(String host) { if ((host != null) && host.indexOf(":") != -1) { // Assume an IPv6 address has been specified and adds the brackets // for the URL. host = host.trim(); if (!host.startsWith("[")) { host = "["+host; } if (!host.endsWith("]")) { host = host + "]"; } } return host; } /** * Returns the String representation of the first value of an attribute in a * LDAP entry. * @param entry the entry. * @param attrName the attribute name. * @return the String representation of the first value of an attribute in a * LDAP entry. * @throws NamingException if there is an error processing the entry. */ static public String getFirstValue(SearchResult entry, String attrName) throws NamingException { String v = null; Attributes attrs = entry.getAttributes(); if (attrs != null) { Attribute attr = attrs.get(attrName); if ((attr != null) && (attr.size() > 0)) { Object o = attr.get(); if (o instanceof String) { v = (String)o; } else { v = String.valueOf(o); } } } return v; } /** * Returns a Set with the String representation of the values of an attribute * in a LDAP entry. The returned Set will never be null. * @param entry the entry. * @param attrName the attribute name. * @return a Set with the String representation of the values of an attribute * in a LDAP entry. * @throws NamingException if there is an error processing the entry. */ static public Set<String> getValues(SearchResult entry, String attrName) throws NamingException { Set<String> values = new HashSet<String>(); Attributes attrs = entry.getAttributes(); if (attrs != null) { Attribute attr = attrs.get(attrName); if (attr != null) { for (int i=0; i<attr.size(); i++) { values.add((String)attr.get(i)); } } } return values; } } opends/src/ads/org/opends/admin/ads/util/ServerLoader.java
New file @@ -0,0 +1,349 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads.util; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.naming.NamingException; import javax.naming.NoPermissionException; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapName; import org.opends.admin.ads.ADSContext; import org.opends.admin.ads.ServerDescriptor; import org.opends.admin.ads.TopologyCacheException; import org.opends.admin.ads.ADSContext.ServerProperty; /** * Class used to load the configuration of a server. Basically the code * uses some provided properties and authentication information to connect * to the server and then generate a ServerDescriptor object based on the * read configuration. */ public class ServerLoader extends Thread { private Map<ServerProperty,Object> serverProperties; private boolean isOver; private boolean isInterrupted; private String lastLdapUrl; private TopologyCacheException lastException; private ServerDescriptor serverDescriptor; private ApplicationTrustManager trustManager; private String dn; private String pwd; private static final Logger LOG = Logger.getLogger(ServerLoader.class.getName()); /** * Constructor. * @param serverProperties the server properties of the server we want to * load. * @param dn the DN that we must use to bind to the server. * @param pwd the password that we must use to bind to the server. * @param trustManager the ApplicationTrustManager to be used when we try * to connect to the server. */ public ServerLoader(Map<ServerProperty,Object> serverProperties, String dn, String pwd, ApplicationTrustManager trustManager) { this.serverProperties = serverProperties; this.dn = dn; this.pwd = pwd; this.trustManager = trustManager; } /** * Returns the ServerDescriptor that could be retrieved. * @return the ServerDescriptor that could be retrieved. */ public ServerDescriptor getServerDescriptor() { if (serverDescriptor == null) { serverDescriptor = ServerDescriptor.createStandalone(serverProperties); } serverDescriptor.setLastException(lastException); return serverDescriptor; } /** * Returns the last exception that occurred while trying to generate * the ServerDescriptor object. * @return the last exception that occurred while trying to generate * the ServerDescriptor object. */ public TopologyCacheException getLastException() { return lastException; } /** * {@inheritDoc} */ public void interrupt() { if (!isOver) { isInterrupted = true; String ldapUrl = getLdapUrl(serverProperties); if (ldapUrl == null) { ldapUrl = getLdapsUrl(serverProperties); } lastException = new TopologyCacheException( TopologyCacheException.Type.TIMEOUT, null, trustManager, ldapUrl); LOG.log(Level.WARNING, "Timeout reading server: "+ldapUrl); } super.interrupt(); } /** * The method where we try to generate the ServerDescriptor object. */ public void run() { lastException = null; InitialLdapContext ctx = null; try { ctx = createContext(); serverDescriptor = ServerDescriptor.createStandalone(ctx); serverDescriptor.setAdsProperties(serverProperties); } catch (NoPermissionException npe) { LOG.log(Level.WARNING, "Permissions error reading server: "+getLastLdapUrl(), npe); if (!isAdministratorDn()) { lastException = new TopologyCacheException( TopologyCacheException.Type.NOT_GLOBAL_ADMINISTRATOR, npe, trustManager, getLastLdapUrl()); } } catch (NamingException ne) { LOG.log(Level.WARNING, "NamingException error reading server: "+getLastLdapUrl(), ne); if (ctx == null) { lastException = new TopologyCacheException( TopologyCacheException.Type.GENERIC_CREATING_CONNECTION, ne, trustManager, getLastLdapUrl()); } else { lastException = new TopologyCacheException( TopologyCacheException.Type.GENERIC_READING_SERVER, ne, trustManager, getLastLdapUrl()); } } catch (Throwable t) { if (!isInterrupted) { LOG.log(Level.WARNING, "Generic error reading server: "+getLastLdapUrl(), t); lastException = new TopologyCacheException(TopologyCacheException.Type.BUG, t); } } finally { isOver = true; try { if (ctx != null) { ctx.close(); } } catch (Throwable t) { } } } /** * Create an InitialLdapContext based in the provide server properties and * authentication data provided in the constructor. * @return an InitialLdapContext based in the provide server properties and * authentication data provided in the constructor. * @throws NamingException if an error occurred while creating the * InitialLdapContext. */ public InitialLdapContext createContext() throws NamingException { InitialLdapContext ctx = null; trustManager.resetLastRefusedItems(); String host = (String)serverProperties.get(ServerProperty.HOST_NAME); trustManager.setHost(host); lastLdapUrl = getLdapsUrl(serverProperties); if (lastLdapUrl == null) { lastLdapUrl = getStartTlsLdapUrl(serverProperties); if (lastLdapUrl == null) { lastLdapUrl = getLdapUrl(serverProperties); ctx = ConnectionUtils.createLdapContext(lastLdapUrl, dn, pwd, ConnectionUtils.getDefaultLDAPTimeout(), null); } else { ctx = ConnectionUtils.createStartTLSContext(lastLdapUrl, dn, pwd, ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager, null); } } else { ctx = ConnectionUtils.createLdapsContext(lastLdapUrl, dn, pwd, ConnectionUtils.getDefaultLDAPTimeout(), null, trustManager); } return ctx; } /** * Returns the last LDAP URL to which we tried to connect. * @return the last LDAP URL to which we tried to connect. */ private String getLastLdapUrl() { return lastLdapUrl; } /** * Returns the non-secure LDAP URL for the given server properties. It * returns NULL if according to the server properties no non-secure LDAP URL * can be generated (LDAP disabled or port not defined). * @param serverProperties the server properties to be used to generate * the non-secure LDAP URL. * @return the non-secure LDAP URL for the given server properties. */ private String getLdapUrl(Map<ServerProperty,Object> serverProperties) { String ldapUrl = null; Object v = serverProperties.get(ServerProperty.LDAP_ENABLED); boolean ldapEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); if (ldapEnabled) { ldapUrl = "ldap://"+getHostNameForLdapUrl(serverProperties)+":"+ serverProperties.get(ServerProperty.LDAP_PORT); } return ldapUrl; } /** * Returns the StartTLS LDAP URL for the given server properties. It * returns NULL if according to the server properties no StartTLS LDAP URL * can be generated (StartTLS disabled or port not defined). * @param serverProperties the server properties to be used to generate * the StartTLS LDAP URL. * @return the StartTLS LDAP URL for the given server properties. */ private String getStartTlsLdapUrl(Map<ServerProperty,Object> serverProperties) { String ldapUrl = null; Object v = serverProperties.get(ServerProperty.LDAP_ENABLED); boolean ldapEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); v = serverProperties.get(ServerProperty.STARTTLS_ENABLED); boolean startTLSEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); if (ldapEnabled && startTLSEnabled) { ldapUrl = "ldaps://"+getHostNameForLdapUrl(serverProperties)+":"+ serverProperties.get(ServerProperty.LDAP_PORT); } return ldapUrl; } /** * Returns the LDAPs URL for the given server properties. It * returns NULL if according to the server properties no LDAPS URL * can be generated (LDAPS disabled or port not defined). * @param serverProperties the server properties to be used to generate * the LDAPS URL. * @return the LDAPS URL for the given server properties. */ private String getLdapsUrl(Map<ServerProperty,Object> serverProperties) { String ldapsUrl = null; Object v = serverProperties.get(ServerProperty.LDAPS_ENABLED); boolean ldapsEnabled = (v != null) && "true".equalsIgnoreCase(v.toString()); if (ldapsEnabled) { ldapsUrl = "ldaps://"+getHostNameForLdapUrl(serverProperties)+":"+ serverProperties.get(ServerProperty.LDAPS_PORT); } return ldapsUrl; } /** * Returns the host name to be used to generate an LDAP URL based on the * contents of the provided server properties. * @param serverProperties the server properties. * @return the host name to be used to generate an LDAP URL based on the * contents of the provided server properties. */ private String getHostNameForLdapUrl( Map<ServerProperty,Object> serverProperties) { String host = (String)serverProperties.get(ServerProperty.HOST_NAME); return ConnectionUtils.getHostNameForLdapUrl(host); } /** * Returns whether the DN provided in the constructor is a Global * Administrator DN or not. * @return <CODE>true</CODE> if the DN provided in the constructor is a Global * Administrator DN and <CODE>false</CODE> otherwise. */ private boolean isAdministratorDn() { boolean isAdministratorDn = false; try { LdapName theDn = new LdapName(dn); LdapName containerDn = new LdapName(ADSContext.getAdministratorContainerDN()); isAdministratorDn = theDn.endsWith(containerDn); } catch (Throwable t) { LOG.log(Level.WARNING, "Error parsing authentication DNs.", t); } return isAdministratorDn; } } opends/src/ads/org/opends/admin/ads/util/TrustedSocketFactory.java
New file @@ -0,0 +1,237 @@ /* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 * * * Portions Copyright 2007 Sun Microsystems, Inc. */ package org.opends.admin.ads.util; import java.io.IOException; import java.net.Socket; import java.net.InetAddress; import java.util.Map; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; import java.security.GeneralSecurityException; import javax.net.SocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLKeyException; import javax.net.ssl.TrustManager; /** * An implementation of SSLSocketFactory. */ public class TrustedSocketFactory extends SSLSocketFactory { static private final Logger LOG = Logger.getLogger(TrustedSocketFactory.class.getName()); private static Map<Thread, TrustManager> hmTrustManager = new HashMap<Thread, TrustManager>(); private static Map<TrustManager, SocketFactory> hmDefaultFactory = new HashMap<TrustManager, SocketFactory>(); private SSLSocketFactory innerFactory; private TrustManager trustManager; /** * Constructor of the TrustedSocketFactory. * @param trustManager the trust manager to use. */ public TrustedSocketFactory(TrustManager trustManager) { this.trustManager = trustManager; } /** * Sets the provided trust manager for the operations in the current thread. * @param trustManager the trust manager to use. */ public static synchronized void setCurrentThreadTrustManager( TrustManager trustManager) { setThreadTrustManager(trustManager, Thread.currentThread()); } /** * Sets the provided trust manager for the operations in the provided thread. * @param trustManager the trust manager to use. * @param thread the thread where we want to use the provided trust manager. */ public static synchronized void setThreadTrustManager( TrustManager trustManager, Thread thread) { TrustManager currentTrustManager = hmTrustManager.get(thread); if (currentTrustManager != null) { hmDefaultFactory.remove(currentTrustManager); hmTrustManager.remove(thread); } if (trustManager != null) { hmTrustManager.put(thread, trustManager); } } // // SocketFactory implementation // /** * {@inheritDoc} */ public static synchronized SocketFactory getDefault() { Thread currentThread = Thread.currentThread(); TrustManager trustManager = hmTrustManager.get(currentThread); SocketFactory result; if (trustManager == null) { LOG.log(Level.SEVERE, "Can't find a trust manager associated to thread " + currentThread); result = new TrustedSocketFactory(null); } else { result = hmDefaultFactory.get(trustManager); if (result == null) { result = new TrustedSocketFactory(trustManager); hmDefaultFactory.put(trustManager, result); } } return result; } /** * {@inheritDoc} */ public Socket createSocket(InetAddress address, int port) throws IOException { return getInnerFactory().createSocket(address, port); } /** * {@inheritDoc} */ public Socket createSocket(InetAddress address, int port, InetAddress clientAddress, int clientPort) throws IOException { return getInnerFactory().createSocket(address, port, clientAddress, clientPort); } /** * {@inheritDoc} */ public Socket createSocket(String host, int port) throws IOException { return getInnerFactory().createSocket(host, port); } /** * {@inheritDoc} */ public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException { return getInnerFactory().createSocket(host, port, clientHost, clientPort); } /** * {@inheritDoc} */ public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { return getInnerFactory().createSocket(s, host, port, autoClose); } /** * {@inheritDoc} */ public String[] getDefaultCipherSuites() { try { return getInnerFactory().getDefaultCipherSuites(); } catch(IOException x) { return new String[0]; } } /** * {@inheritDoc} */ public String[] getSupportedCipherSuites() { try { return getInnerFactory().getSupportedCipherSuites(); } catch(IOException x) { return new String[0]; } } // // Private // private SSLSocketFactory getInnerFactory() throws IOException { if (innerFactory == null) { String algorithm = "TLSv1"; SSLKeyException xx; try { SSLContext sslCtx = SSLContext.getInstance(algorithm); if (trustManager == null) { LOG.log(Level.SEVERE, "Warning : no trust for this factory"); sslCtx.init(null, null, null); // No certif -> no SSL connection } else { sslCtx.init(null, new TrustManager[] { trustManager }, null ); } innerFactory = sslCtx.getSocketFactory(); } catch(GeneralSecurityException x) { xx = new SSLKeyException("Failed to create SSLContext for " + algorithm); xx.initCause(x); throw xx; } } return innerFactory; } } opends/src/quicksetup/org/opends/quicksetup/installer/Installer.java
@@ -44,8 +44,6 @@ import org.opends.admin.ads.ADSContext; import org.opends.admin.ads.ADSContextException; import org.opends.admin.ads.ReplicaDescriptor; import org.opends.admin.ads.ServerDescriptor; import org.opends.admin.ads.SuffixDescriptor; import org.opends.quicksetup.ui.*; import org.opends.quicksetup.util.Utils; @@ -1327,6 +1325,7 @@ notifyListeners(getFormattedWithPoints(getMsg("creating-ads"))); Map<ADSContext.ServerProperty, Object> serverProperties = getNewServerProperties(); /* try { ADSContext.createOfflineAdminData(serverProperties, @@ -1338,6 +1337,7 @@ ApplicationException.Type.CONFIGURATION_ERROR, getMsg("local-ads-exception"), ace); } */ notifyListeners(getFormattedDone()); } } @@ -1382,6 +1382,7 @@ Map<ADSContext.ServerProperty, Object> serverProperties = new HashMap<ADSContext.ServerProperty, Object>(); // TODO: this might not work /* try { serverProperties.put(ADSContext.ServerProperty.HOSTNAME, @@ -1409,8 +1410,9 @@ String serverID = serverProperties.get(ADSContext.ServerProperty.HOSTNAME)+ ":"+getUserData().getServerPort(); */ /* TODO: do we want to ask this specifically to the user? */ serverProperties.put(ADSContext.ServerProperty.ID, serverID); //serverProperties.put(ADSContext.ServerProperty.ID, serverID); serverProperties.put(ADSContext.ServerProperty.HOST_OS, Utils.getOSString()); @@ -2269,6 +2271,7 @@ private Set<SuffixDescriptor> staticSuffixes() { /* ServerDescriptor server1 = new ServerDescriptor(); Map<ADSContext.ServerProperty, Object> serverProp1 = new HashMap<ADSContext.ServerProperty, Object>(); @@ -2356,13 +2359,14 @@ replicas4.add(replica8); suffix4.setReplicas(replicas4); */ Set<SuffixDescriptor> suffixes = new HashSet<SuffixDescriptor>(); /* suffixes.add(suffix1); suffixes.add(suffix2); suffixes.add(suffix3); suffixes.add(suffix4); */ //suffixes.clear(); return suffixes; opends/src/quicksetup/org/opends/quicksetup/installer/ui/SuffixesToReplicatePanel.java
@@ -51,7 +51,6 @@ import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import org.opends.admin.ads.ADSContext; import org.opends.admin.ads.ReplicaDescriptor; import org.opends.admin.ads.ServerDescriptor; import org.opends.admin.ads.SuffixDescriptor; @@ -442,53 +441,7 @@ //TODO: only handles server that have ADSProperties. private String getServerDisplay(ServerDescriptor server) { String display; String hostName = (String)server.getAdsProperties().get( ADSContext.ServerProperty.HOSTNAME); boolean secure = false; int port = -1; if (hostName == null) { hostName = getMsg("suffix-list-unknown-label"); } if (!"true".equalsIgnoreCase((String)server.getAdsProperties().get( ADSContext.ServerProperty.LDAP_ENABLED))) { if ("true".equalsIgnoreCase((String)server.getAdsProperties().get( ADSContext.ServerProperty.LDAPS_ENABLED))) { secure = true; } } try { if (secure) { port = Integer.parseInt((String)server.getAdsProperties().get( ADSContext.ServerProperty.SECURE_PORT)); } else { port = Integer.parseInt((String)server.getAdsProperties().get( ADSContext.ServerProperty.PORT)); } } catch (Throwable t) { } if (port == -1) { display = hostName+":"+getMsg("suffix-list-unknown-label"); } else { display = hostName+":"+port; } return display; return server.getHostPort(true); } private TreeSet<SuffixDescriptor> orderSuffixes(