001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2007-2008 Sun Microsystems, Inc. 026 */ 027 028 package org.opends.admin.ads; 029 030 import java.util.ArrayList; 031 import java.util.HashMap; 032 import java.util.HashSet; 033 import java.util.LinkedHashSet; 034 import java.util.Map; 035 import java.util.Set; 036 037 import javax.naming.NameNotFoundException; 038 import javax.naming.NamingEnumeration; 039 import javax.naming.NamingException; 040 import javax.naming.NameAlreadyBoundException; 041 import javax.naming.directory.*; 042 import javax.naming.ldap.InitialLdapContext; 043 import javax.naming.ldap.LdapName; 044 import javax.naming.ldap.Rdn; 045 046 import org.opends.admin.ads.util.ConnectionUtils; 047 import org.opends.quicksetup.util.Utils; 048 049 /** 050 * The object of this class represent an OpenDS server. 051 */ 052 public class ServerDescriptor 053 { 054 private Map<ADSContext.ServerProperty, Object> adsProperties = 055 new HashMap<ADSContext.ServerProperty, Object>(); 056 private Set<ReplicaDescriptor> replicas = new HashSet<ReplicaDescriptor>(); 057 private Map<ServerProperty, Object> serverProperties = 058 new HashMap<ServerProperty, Object>(); 059 private TopologyCacheException lastException; 060 /** 061 * Enumeration containing the different server properties that we can keep in 062 * the ServerProperty object. 063 */ 064 public enum ServerProperty 065 { 066 /** 067 * The associated value is a String. 068 */ 069 HOST_NAME, 070 /** 071 * The associated value is an ArrayList of Integer. 072 */ 073 LDAP_PORT, 074 /** 075 * The associated value is an ArrayList of Integer. 076 */ 077 LDAPS_PORT, 078 /** 079 * The associated value is an ArrayList of Boolean. 080 */ 081 LDAP_ENABLED, 082 /** 083 * The associated value is an ArrayList of Boolean. 084 */ 085 LDAPS_ENABLED, 086 /** 087 * The associated value is an ArrayList of Boolean. 088 */ 089 STARTTLS_ENABLED, 090 /** 091 * The associated value is an ArrayList of Integer. 092 */ 093 JMX_PORT, 094 /** 095 * The associated value is an ArrayList of Integer. 096 */ 097 JMXS_PORT, 098 /** 099 * The associated value is an ArrayList of Boolean. 100 */ 101 JMX_ENABLED, 102 /** 103 * The associated value is an ArrayList of Boolean. 104 */ 105 JMXS_ENABLED, 106 /** 107 * The associated value is an Integer. 108 */ 109 REPLICATION_SERVER_PORT, 110 /** 111 * The associated value is a Boolean. 112 */ 113 IS_REPLICATION_SERVER, 114 /** 115 * The associated value is a Boolean. 116 */ 117 IS_REPLICATION_ENABLED, 118 /** 119 * The associated value is a Boolean. 120 */ 121 IS_REPLICATION_SECURE, 122 /** 123 * List of servers specified in the Replication Server configuration. 124 * This is a Set of String. 125 */ 126 EXTERNAL_REPLICATION_SERVERS, 127 /** 128 * The associated value is an Integer. 129 */ 130 REPLICATION_SERVER_ID, 131 /** 132 * The instance key-pair public-key certificate. The associated value is a 133 * byte[] (ds-cfg-public-key-certificate;binary). 134 */ 135 INSTANCE_PUBLIC_KEY_CERTIFICATE, 136 /** 137 * The schema generation ID. 138 */ 139 SCHEMA_GENERATION_ID 140 } 141 142 private ServerDescriptor() 143 { 144 } 145 146 /** 147 * Returns the replicas contained on the server. 148 * @return the replicas contained on the server. 149 */ 150 public Set<ReplicaDescriptor> getReplicas() 151 { 152 Set<ReplicaDescriptor> copy = new HashSet<ReplicaDescriptor>(); 153 copy.addAll(replicas); 154 return copy; 155 } 156 157 /** 158 * Sets the replicas contained on the server. 159 * @param replicas the replicas contained on the server. 160 */ 161 public void setReplicas(Set<ReplicaDescriptor> replicas) 162 { 163 this.replicas.clear(); 164 this.replicas.addAll(replicas); 165 } 166 167 /** 168 * Returns a Map containing the ADS properties of the server. 169 * @return a Map containing the ADS properties of the server. 170 */ 171 public Map<ADSContext.ServerProperty, Object> getAdsProperties() 172 { 173 return adsProperties; 174 } 175 176 /** 177 * Returns a Map containing the properties of the server. 178 * @return a Map containing the properties of the server. 179 */ 180 public Map<ServerProperty, Object> getServerProperties() 181 { 182 return serverProperties; 183 } 184 185 /** 186 * Tells whether this server is registered in the ADS or not. 187 * @return <CODE>true</CODE> if the server is registered in the ADS and 188 * <CODE>false</CODE> otherwise. 189 */ 190 public boolean isRegistered() 191 { 192 return !adsProperties.isEmpty(); 193 } 194 195 /** 196 * Tells whether this server is a replication server or not. 197 * @return <CODE>true</CODE> if the server is a replication server and 198 * <CODE>false</CODE> otherwise. 199 */ 200 public boolean isReplicationServer() 201 { 202 return Boolean.TRUE.equals( 203 serverProperties.get(ServerProperty.IS_REPLICATION_SERVER)); 204 } 205 206 /** 207 * Returns the String representation of this replication server based 208 * on the information we have ("hostname":"replication port") and 209 * <CODE>null</CODE> if this is not a replication server. 210 * @return the String representation of this replication server based 211 * on the information we have ("hostname":"replication port") and 212 * <CODE>null</CODE> if this is not a replication server. 213 */ 214 public String getReplicationServerHostPort() 215 { 216 String hostPort = null; 217 if (isReplicationServer()) 218 { 219 hostPort = getHostName().toLowerCase()+ ":" + getReplicationServerPort(); 220 } 221 return hostPort; 222 } 223 224 /** 225 * Returns the replication server ID of this server and -1 if this is not a 226 * replications server. 227 * @return the replication server ID of this server and -1 if this is not a 228 * replications server. 229 */ 230 public int getReplicationServerId() 231 { 232 int port = -1; 233 if (isReplicationServer()) 234 { 235 port = (Integer)serverProperties.get( 236 ServerProperty.REPLICATION_SERVER_ID); 237 } 238 return port; 239 } 240 241 /** 242 * Returns the replication port of this server and -1 if this is not a 243 * replications server. 244 * @return the replication port of this server and -1 if this is not a 245 * replications server. 246 */ 247 public int getReplicationServerPort() 248 { 249 int port = -1; 250 if (isReplicationServer()) 251 { 252 port = (Integer)serverProperties.get( 253 ServerProperty.REPLICATION_SERVER_PORT); 254 } 255 return port; 256 } 257 258 /** 259 * Returns whether the communication with the replication port on the server 260 * is encrypted or not. 261 * @return <CODE>true</CODE> if the communication with the replication port on 262 * the server is encrypted and <CODE>false</CODE> otherwise. 263 */ 264 public boolean isReplicationSecure() 265 { 266 boolean isReplicationSecure; 267 if (isReplicationServer()) 268 { 269 isReplicationSecure = Boolean.TRUE.equals(serverProperties.get( 270 ServerProperty.IS_REPLICATION_SECURE)); 271 } 272 else 273 { 274 isReplicationSecure = false; 275 } 276 return isReplicationSecure; 277 } 278 279 /** 280 * Sets the ADS properties of the server. 281 * @param adsProperties a Map containing the ADS properties of the server. 282 */ 283 public void setAdsProperties( 284 Map<ADSContext.ServerProperty, Object> adsProperties) 285 { 286 this.adsProperties = adsProperties; 287 } 288 289 /** 290 * Returns the host name of the server. 291 * @return the host name of the server. 292 */ 293 public String getHostName() 294 { 295 String host = (String)serverProperties.get(ServerProperty.HOST_NAME); 296 if (host == null) 297 { 298 host = (String)adsProperties.get(ADSContext.ServerProperty.HOST_NAME); 299 } 300 return host; 301 } 302 303 /** 304 * Returns the URL to access this server using LDAP. Returns 305 * <CODE>null</CODE> if the server is not configured to listen on an LDAP 306 * port. 307 * @return the URL to access this server using LDAP. 308 */ 309 public String getLDAPURL() 310 { 311 String ldapUrl = null; 312 String host = getHostName(); 313 int port = -1; 314 315 if (!serverProperties.isEmpty()) 316 { 317 ArrayList s = (ArrayList)serverProperties.get( 318 ServerProperty.LDAP_ENABLED); 319 ArrayList p = (ArrayList)serverProperties.get( 320 ServerProperty.LDAP_PORT); 321 if (s != null) 322 { 323 for (int i=0; i<s.size(); i++) 324 { 325 if (Boolean.TRUE.equals(s.get(i))) 326 { 327 port = (Integer)p.get(i); 328 break; 329 } 330 } 331 } 332 } 333 if (port != -1) 334 { 335 ldapUrl = ConnectionUtils.getLDAPUrl(host, port, false); 336 } 337 return ldapUrl; 338 } 339 340 /** 341 * Returns the URL to access this server using LDAPS. Returns 342 * <CODE>null</CODE> if the server is not configured to listen on an LDAPS 343 * port. 344 * @return the URL to access this server using LDAP. 345 */ 346 public String getLDAPsURL() 347 { 348 String ldapsUrl = null; 349 String host = getHostName(); 350 int port = -1; 351 352 if (!serverProperties.isEmpty()) 353 { 354 ArrayList s = (ArrayList)serverProperties.get( 355 ServerProperty.LDAPS_ENABLED); 356 ArrayList p = (ArrayList)serverProperties.get( 357 ServerProperty.LDAPS_PORT); 358 if (s != null) 359 { 360 for (int i=0; i<s.size(); i++) 361 { 362 if (Boolean.TRUE.equals(s.get(i))) 363 { 364 port = (Integer)p.get(i); 365 break; 366 } 367 } 368 } 369 } 370 if (port != -1) 371 { 372 ldapsUrl = ConnectionUtils.getLDAPUrl(host, port, true); 373 } 374 return ldapsUrl; 375 } 376 377 /** 378 * Returns a String of type host-name:port-number for the server. If 379 * the provided securePreferred is set to true the port that will be used 380 * (if LDAPS is enabled) will be the LDAPS port. 381 * @param securePreferred whether to try to use the secure port as part 382 * of the returning String or not. 383 * @return a String of type host-name:port-number for the server. 384 */ 385 public String getHostPort(boolean securePreferred) 386 { 387 String host = getHostName(); 388 int port = -1; 389 390 if (!serverProperties.isEmpty()) 391 { 392 ArrayList s = (ArrayList)serverProperties.get( 393 ServerProperty.LDAP_ENABLED); 394 ArrayList p = (ArrayList)serverProperties.get( 395 ServerProperty.LDAP_PORT); 396 if (s != null) 397 { 398 for (int i=0; i<s.size(); i++) 399 { 400 if (Boolean.TRUE.equals(s.get(i))) 401 { 402 port = (Integer)p.get(i); 403 break; 404 } 405 } 406 } 407 if (securePreferred) 408 { 409 s = (ArrayList)serverProperties.get( 410 ServerProperty.LDAPS_ENABLED); 411 p = (ArrayList)serverProperties.get(ServerProperty.LDAPS_PORT); 412 if (s != null) 413 { 414 for (int i=0; i<s.size(); i++) 415 { 416 if (Boolean.TRUE.equals(s.get(i))) 417 { 418 port = (Integer)p.get(i); 419 break; 420 } 421 } 422 } 423 } 424 } 425 else 426 { 427 boolean secure; 428 429 Object v = adsProperties.get(ADSContext.ServerProperty.LDAPS_ENABLED); 430 secure = securePreferred && "true".equalsIgnoreCase(String.valueOf(v)); 431 try 432 { 433 if (secure) 434 { 435 port = Integer.parseInt((String)adsProperties.get( 436 ADSContext.ServerProperty.LDAPS_PORT)); 437 } 438 else 439 { 440 port = Integer.parseInt((String)adsProperties.get( 441 ADSContext.ServerProperty.LDAP_PORT)); 442 } 443 } 444 catch (Throwable t) 445 { 446 /* ignore */ 447 } 448 } 449 return host + ":" + port; 450 } 451 452 /** 453 * Returns an Id that is unique for this server. 454 * @return an Id that is unique for this server. 455 */ 456 public String getId() 457 { 458 StringBuilder buf = new StringBuilder(); 459 if (serverProperties.size() > 0) 460 { 461 buf.append(serverProperties.get(ServerProperty.HOST_NAME)); 462 ServerProperty [] props = 463 { 464 ServerProperty.LDAP_PORT, ServerProperty.LDAPS_PORT, 465 ServerProperty.LDAP_ENABLED, ServerProperty.LDAPS_ENABLED 466 }; 467 for (ServerProperty prop : props) { 468 ArrayList s = (ArrayList) serverProperties.get(prop); 469 for (Object o : s) { 470 buf.append(":").append(o); 471 } 472 } 473 } 474 else 475 { 476 ADSContext.ServerProperty[] props = 477 { 478 ADSContext.ServerProperty.HOST_NAME, 479 ADSContext.ServerProperty.LDAP_PORT, 480 ADSContext.ServerProperty.LDAPS_PORT, 481 ADSContext.ServerProperty.LDAP_ENABLED, 482 ADSContext.ServerProperty.LDAPS_ENABLED 483 }; 484 for (int i=0; i<props.length; i++) 485 { 486 if (i != 0) 487 { 488 buf.append(":"); 489 } 490 buf.append(adsProperties.get(props[i])); 491 } 492 } 493 return buf.toString(); 494 } 495 496 /** 497 * Returns the instance-key public-key certificate retrieved from the 498 * truststore backend of the instance referenced through this descriptor. 499 * 500 * @return The public-key certificate of the instance. 501 */ 502 public byte[] getInstancePublicKeyCertificate() 503 { 504 return((byte[]) 505 serverProperties.get(ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE)); 506 } 507 508 /** 509 * Returns the schema generation ID of the server. 510 * @return the schema generation ID of the server. 511 */ 512 public String getSchemaReplicationID() 513 { 514 return (String)serverProperties.get(ServerProperty.SCHEMA_GENERATION_ID); 515 } 516 517 /** 518 * Returns the last exception that was encountered reading the configuration 519 * of the server. Returns null if there was no problem loading the 520 * configuration of the server. 521 * @return the last exception that was encountered reading the configuration 522 * of the server. Returns null if there was no problem loading the 523 * configuration of the server. 524 */ 525 public TopologyCacheException getLastException() 526 { 527 return lastException; 528 } 529 530 /** 531 * Sets the last exception that occurred while reading the configuration of 532 * the server. 533 * @param lastException the last exception that occurred while reading the 534 * configuration of the server. 535 */ 536 public void setLastException(TopologyCacheException lastException) 537 { 538 this.lastException = lastException; 539 } 540 541 /** 542 * This methods updates the ADS properties (the ones that were read from 543 * the ADS) with the contents of the server properties (the ones that were 544 * read directly from the server). 545 */ 546 public void updateAdsPropertiesWithServerProperties() 547 { 548 adsProperties.put(ADSContext.ServerProperty.HOST_NAME, getHostName()); 549 ServerProperty[][] sProps = 550 { 551 {ServerProperty.LDAP_ENABLED, ServerProperty.LDAP_PORT}, 552 {ServerProperty.LDAPS_ENABLED, ServerProperty.LDAPS_PORT}, 553 {ServerProperty.JMX_ENABLED, ServerProperty.JMX_PORT}, 554 {ServerProperty.JMXS_ENABLED, ServerProperty.JMXS_PORT} 555 }; 556 ADSContext.ServerProperty[][] adsProps = 557 { 558 {ADSContext.ServerProperty.LDAP_ENABLED, 559 ADSContext.ServerProperty.LDAP_PORT}, 560 {ADSContext.ServerProperty.LDAPS_ENABLED, 561 ADSContext.ServerProperty.LDAPS_PORT}, 562 {ADSContext.ServerProperty.JMX_ENABLED, 563 ADSContext.ServerProperty.JMX_PORT}, 564 {ADSContext.ServerProperty.JMXS_ENABLED, 565 ADSContext.ServerProperty.JMXS_PORT} 566 }; 567 568 for (int i=0; i<sProps.length; i++) 569 { 570 ArrayList s = (ArrayList)serverProperties.get(sProps[i][0]); 571 ArrayList p = (ArrayList)serverProperties.get(sProps[i][1]); 572 if (s != null) 573 { 574 int port = -1; 575 for (int j=0; j<s.size(); j++) 576 { 577 if (Boolean.TRUE.equals(s.get(j))) 578 { 579 port = (Integer)p.get(j); 580 break; 581 } 582 } 583 if (port == -1) 584 { 585 adsProperties.put(adsProps[i][0], "false"); 586 if (p.size() > 0) 587 { 588 port = (Integer)p.iterator().next(); 589 } 590 } 591 else 592 { 593 adsProperties.put(adsProps[i][0], "true"); 594 } 595 adsProperties.put(adsProps[i][1], String.valueOf(port)); 596 } 597 } 598 599 ArrayList array = (ArrayList)serverProperties.get( 600 ServerProperty.STARTTLS_ENABLED); 601 boolean startTLSEnabled = false; 602 if ((array != null) && !array.isEmpty()) 603 { 604 startTLSEnabled = Boolean.TRUE.equals(array.get(array.size() -1)); 605 } 606 adsProperties.put(ADSContext.ServerProperty.STARTTLS_ENABLED, 607 startTLSEnabled ? "true" : "false"); 608 adsProperties.put(ADSContext.ServerProperty.ID, getHostPort(true)); 609 adsProperties.put(ADSContext.ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE, 610 getInstancePublicKeyCertificate()); 611 } 612 613 /** 614 * Creates a ServerDescriptor object based on some ADS properties provided. 615 * @param adsProperties the ADS properties of the server. 616 * @return a ServerDescriptor object that corresponds to the provided ADS 617 * properties. 618 */ 619 public static ServerDescriptor createStandalone( 620 Map<ADSContext.ServerProperty, Object> adsProperties) 621 { 622 ServerDescriptor desc = new ServerDescriptor(); 623 desc.setAdsProperties(adsProperties); 624 return desc; 625 } 626 627 /** 628 * Creates a ServerDescriptor object based on the configuration that we read 629 * using the provided InitialLdapContext. 630 * @param ctx the InitialLdapContext that will be used to read the 631 * configuration of the server. 632 * @param filter the topology cache filter describing the information that 633 * must be retrieved. 634 * @return a ServerDescriptor object that corresponds to the read 635 * configuration. 636 * @throws NamingException if a problem occurred reading the server 637 * configuration. 638 */ 639 public static ServerDescriptor createStandalone(InitialLdapContext ctx, 640 TopologyCacheFilter filter) 641 throws NamingException 642 { 643 ServerDescriptor desc = new ServerDescriptor(); 644 645 646 updateLdapConfiguration(desc, ctx, filter); 647 updateJmxConfiguration(desc, ctx, filter); 648 updateReplicas(desc, ctx, filter); 649 updateReplication(desc, ctx, filter); 650 updatePublicKeyCertificate(desc, ctx, filter); 651 updateMiscellaneous(desc, ctx, filter); 652 653 desc.serverProperties.put(ServerProperty.HOST_NAME, 654 ConnectionUtils.getHostName(ctx)); 655 656 return desc; 657 } 658 659 private static void updateLdapConfiguration(ServerDescriptor desc, 660 InitialLdapContext ctx, TopologyCacheFilter cacheFilter) 661 throws NamingException 662 { 663 SearchControls ctls = new SearchControls(); 664 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); 665 ctls.setReturningAttributes( 666 new String[] { 667 "ds-cfg-enabled", 668 "ds-cfg-listen-address", 669 "ds-cfg-listen-port", 670 "ds-cfg-use-ssl", 671 "ds-cfg-allow-start-tls", 672 "objectclass" 673 }); 674 String filter = "(objectclass=ds-cfg-ldap-connection-handler)"; 675 676 LdapName jndiName = new LdapName("cn=config"); 677 NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); 678 679 ArrayList<Integer> ldapPorts = new ArrayList<Integer>(); 680 ArrayList<Integer> ldapsPorts = new ArrayList<Integer>(); 681 ArrayList<Boolean> ldapEnabled = new ArrayList<Boolean>(); 682 ArrayList<Boolean> ldapsEnabled = new ArrayList<Boolean>(); 683 ArrayList<Boolean> startTLSEnabled = new ArrayList<Boolean>(); 684 685 desc.serverProperties.put(ServerProperty.LDAP_PORT, ldapPorts); 686 desc.serverProperties.put(ServerProperty.LDAPS_PORT, ldapsPorts); 687 desc.serverProperties.put(ServerProperty.LDAP_ENABLED, ldapEnabled); 688 desc.serverProperties.put(ServerProperty.LDAPS_ENABLED, ldapsEnabled); 689 desc.serverProperties.put(ServerProperty.STARTTLS_ENABLED, startTLSEnabled); 690 691 while(listeners.hasMore()) 692 { 693 SearchResult sr = (SearchResult)listeners.next(); 694 695 String port = getFirstValue(sr, "ds-cfg-listen-port"); 696 697 boolean isSecure = "true".equalsIgnoreCase( 698 getFirstValue(sr, "ds-cfg-use-ssl")); 699 700 boolean enabled = "true".equalsIgnoreCase( 701 getFirstValue(sr, "ds-cfg-enabled")); 702 if (isSecure) 703 { 704 ldapsPorts.add(new Integer(port)); 705 ldapsEnabled.add(enabled); 706 } 707 else 708 { 709 ldapPorts.add(new Integer(port)); 710 ldapEnabled.add(enabled); 711 enabled = "true".equalsIgnoreCase( 712 getFirstValue(sr, "ds-cfg-allow-start-tls")); 713 startTLSEnabled.add(enabled); 714 } 715 } 716 } 717 718 private static void updateJmxConfiguration(ServerDescriptor desc, 719 InitialLdapContext ctx, TopologyCacheFilter cacheFilter) 720 throws NamingException 721 { 722 SearchControls ctls = new SearchControls(); 723 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); 724 ctls.setReturningAttributes( 725 new String[] { 726 "ds-cfg-enabled", 727 "ds-cfg-listen-address", 728 "ds-cfg-listen-port", 729 "ds-cfg-use-ssl", 730 "objectclass" 731 }); 732 String filter = "(objectclass=ds-cfg-jmx-connection-handler)"; 733 734 LdapName jndiName = new LdapName("cn=config"); 735 NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); 736 737 ArrayList<Integer> jmxPorts = new ArrayList<Integer>(); 738 ArrayList<Integer> jmxsPorts = new ArrayList<Integer>(); 739 ArrayList<Boolean> jmxEnabled = new ArrayList<Boolean>(); 740 ArrayList<Boolean> jmxsEnabled = new ArrayList<Boolean>(); 741 742 desc.serverProperties.put(ServerProperty.JMX_PORT, jmxPorts); 743 desc.serverProperties.put(ServerProperty.JMXS_PORT, jmxsPorts); 744 desc.serverProperties.put(ServerProperty.JMX_ENABLED, jmxEnabled); 745 desc.serverProperties.put(ServerProperty.JMXS_ENABLED, jmxsEnabled); 746 747 while(listeners.hasMore()) 748 { 749 SearchResult sr = (SearchResult)listeners.next(); 750 751 String port = getFirstValue(sr, "ds-cfg-listen-port"); 752 753 boolean isSecure = "true".equalsIgnoreCase( 754 getFirstValue(sr, "ds-cfg-use-ssl")); 755 756 boolean enabled = "true".equalsIgnoreCase( 757 getFirstValue(sr, "ds-cfg-enabled")); 758 if (isSecure) 759 { 760 jmxsPorts.add(new Integer(port)); 761 jmxsEnabled.add(enabled); 762 } 763 else 764 { 765 jmxPorts.add(new Integer(port)); 766 jmxEnabled.add(enabled); 767 } 768 } 769 } 770 771 private static void updateReplicas(ServerDescriptor desc, 772 InitialLdapContext ctx, TopologyCacheFilter cacheFilter) 773 throws NamingException 774 { 775 if (!cacheFilter.searchBaseDNInformation()) 776 { 777 return; 778 } 779 SearchControls ctls = new SearchControls(); 780 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); 781 ctls.setReturningAttributes( 782 new String[] { 783 "ds-cfg-base-dn", 784 "ds-cfg-backend-id" 785 }); 786 String filter = "(objectclass=ds-cfg-backend)"; 787 788 LdapName jndiName = new LdapName("cn=config"); 789 NamingEnumeration databases = ctx.search(jndiName, filter, ctls); 790 791 while(databases.hasMore()) 792 { 793 SearchResult sr = (SearchResult)databases.next(); 794 795 String id = getFirstValue(sr, "ds-cfg-backend-id"); 796 797 if (!isConfigBackend(id) || isSchemaBackend(id)) 798 { 799 Set<String> baseDns = getValues(sr, "ds-cfg-base-dn"); 800 801 Set<String> entries; 802 if (cacheFilter.searchMonitoringInformation()) 803 { 804 entries = getBaseDNEntryCount(ctx, id); 805 } 806 else 807 { 808 entries = new HashSet<String>(); 809 } 810 811 Set<ReplicaDescriptor> replicas = desc.getReplicas(); 812 for (String baseDn : baseDns) 813 { 814 boolean addReplica = cacheFilter.searchAllBaseDNs(); 815 if (!addReplica) 816 { 817 for (String dn : cacheFilter.getBaseDNsToSearch()) 818 { 819 addReplica = Utils.areDnsEqual(dn, baseDn); 820 if (addReplica) 821 { 822 break; 823 } 824 } 825 } 826 if(addReplica) 827 { 828 SuffixDescriptor suffix = new SuffixDescriptor(); 829 suffix.setDN(baseDn); 830 ReplicaDescriptor replica = new ReplicaDescriptor(); 831 replica.setServer(desc); 832 replicas.add(replica); 833 HashSet<ReplicaDescriptor> r = new HashSet<ReplicaDescriptor>(); 834 r.add(replica); 835 suffix.setReplicas(r); 836 replica.setSuffix(suffix); 837 int nEntries = -1; 838 for (String s : entries) 839 { 840 int index = s.indexOf(" "); 841 if (index != -1) 842 { 843 String dn = s.substring(index + 1); 844 if (Utils.areDnsEqual(baseDn, dn)) 845 { 846 try 847 { 848 nEntries = Integer.parseInt(s.substring(0, index)); 849 } 850 catch (Throwable t) 851 { 852 /* Ignore */ 853 } 854 break; 855 } 856 } 857 } 858 replica.setEntries(nEntries); 859 } 860 } 861 desc.setReplicas(replicas); 862 } 863 } 864 } 865 866 private static void updateReplication(ServerDescriptor desc, 867 InitialLdapContext ctx, TopologyCacheFilter cacheFilter) 868 throws NamingException 869 { 870 boolean replicationEnabled = false; 871 boolean oneDomainReplicated = false; 872 SearchControls ctls = new SearchControls(); 873 ctls.setSearchScope(SearchControls.OBJECT_SCOPE); 874 ctls.setReturningAttributes( 875 new String[] { 876 "ds-cfg-enabled" 877 }); 878 String filter = "(objectclass=ds-cfg-synchronization-provider)"; 879 880 LdapName jndiName = new LdapName( 881 "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config"); 882 883 try 884 { 885 NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls); 886 887 while(syncProviders.hasMore()) 888 { 889 SearchResult sr = (SearchResult)syncProviders.next(); 890 891 if ("true".equalsIgnoreCase(getFirstValue(sr, 892 "ds-cfg-enabled"))) 893 { 894 replicationEnabled = true; 895 } 896 } 897 } 898 catch (NameNotFoundException nse) 899 { 900 /* ignore */ 901 } 902 desc.serverProperties.put(ServerProperty.IS_REPLICATION_ENABLED, 903 replicationEnabled ? Boolean.TRUE : Boolean.FALSE); 904 905 if (cacheFilter.searchBaseDNInformation()) 906 { 907 ctls = new SearchControls(); 908 ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); 909 ctls.setReturningAttributes( 910 new String[] { 911 "ds-cfg-base-dn", 912 "ds-cfg-replication-server", 913 "ds-cfg-server-id" 914 }); 915 filter = "(objectclass=ds-cfg-replication-domain)"; 916 917 jndiName = new LdapName( 918 "cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config"); 919 920 try 921 { 922 NamingEnumeration syncProviders = ctx.search(jndiName, filter, ctls); 923 924 while(syncProviders.hasMore()) 925 { 926 SearchResult sr = (SearchResult)syncProviders.next(); 927 928 int id = Integer.parseInt( 929 getFirstValue(sr, "ds-cfg-server-id")); 930 Set<String> replicationServers = getValues(sr, 931 "ds-cfg-replication-server"); 932 Set<String> dns = getValues(sr, "ds-cfg-base-dn"); 933 oneDomainReplicated = dns.size() > 0; 934 for (String dn : dns) 935 { 936 for (ReplicaDescriptor replica : desc.getReplicas()) 937 { 938 if (areDnsEqual(replica.getSuffix().getDN(), dn)) 939 { 940 replica.setReplicationId(id); 941 // Keep the values of the replication servers in lower case 942 // to make use of Sets as String simpler. 943 LinkedHashSet<String> repServers = new LinkedHashSet<String>(); 944 for (String s: replicationServers) 945 { 946 repServers.add(s.toLowerCase()); 947 } 948 replica.setReplicationServers(repServers); 949 } 950 } 951 } 952 } 953 } 954 catch (NameNotFoundException nse) 955 { 956 /* ignore */ 957 } 958 } 959 960 ctls = new SearchControls(); 961 ctls.setSearchScope(SearchControls.OBJECT_SCOPE); 962 ctls.setReturningAttributes( 963 new String[] { 964 "ds-cfg-replication-port", "ds-cfg-replication-server", 965 "ds-cfg-replication-server-id" 966 }); 967 filter = "(objectclass=ds-cfg-replication-server)"; 968 969 jndiName = new LdapName("cn=Replication Server,cn=Multimaster "+ 970 "Synchronization,cn=Synchronization Providers,cn=config"); 971 972 desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER, 973 Boolean.FALSE); 974 try 975 { 976 NamingEnumeration entries = ctx.search(jndiName, filter, ctls); 977 978 while(entries.hasMore()) 979 { 980 SearchResult sr = (SearchResult)entries.next(); 981 982 desc.serverProperties.put(ServerProperty.IS_REPLICATION_SERVER, 983 Boolean.TRUE); 984 String v = getFirstValue(sr, "ds-cfg-replication-port"); 985 desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_PORT, 986 Integer.parseInt(v)); 987 v = getFirstValue(sr, "ds-cfg-replication-server-id"); 988 desc.serverProperties.put(ServerProperty.REPLICATION_SERVER_ID, 989 Integer.parseInt(v)); 990 Set<String> values = getValues(sr, "ds-cfg-replication-server"); 991 // Keep the values of the replication servers in lower case 992 // to make use of Sets as String simpler. 993 LinkedHashSet<String> repServers = new LinkedHashSet<String>(); 994 for (String s: values) 995 { 996 repServers.add(s.toLowerCase()); 997 } 998 desc.serverProperties.put(ServerProperty.EXTERNAL_REPLICATION_SERVERS, 999 repServers); 1000 } 1001 } 1002 catch (NameNotFoundException nse) 1003 { 1004 /* ignore */ 1005 } 1006 1007 if (cacheFilter.searchMonitoringInformation()) 1008 { 1009 ctls = new SearchControls(); 1010 ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE); 1011 ctls.setReturningAttributes( 1012 new String[] { 1013 "approx-older-change-not-synchronized-millis", "missing-changes", 1014 "base-dn", "server-id" 1015 }); 1016 filter = "(missing-changes=*)"; 1017 1018 jndiName = new LdapName("cn=monitor"); 1019 1020 if (oneDomainReplicated) 1021 { 1022 try 1023 { 1024 NamingEnumeration monitorEntries = ctx.search(jndiName, filter, ctls); 1025 1026 while(monitorEntries.hasMore()) 1027 { 1028 SearchResult sr = (SearchResult)monitorEntries.next(); 1029 1030 String dn = getFirstValue(sr, "base-dn"); 1031 int replicaId = -1; 1032 try 1033 { 1034 replicaId = new Integer(getFirstValue(sr, "server-id")); 1035 } 1036 catch (Throwable t) 1037 { 1038 } 1039 1040 for (ReplicaDescriptor replica: desc.getReplicas()) 1041 { 1042 if (Utils.areDnsEqual(dn, replica.getSuffix().getDN()) && 1043 replica.isReplicated() && 1044 (replica.getReplicationId() == replicaId)) 1045 { 1046 try 1047 { 1048 replica.setAgeOfOldestMissingChange( 1049 new Long(getFirstValue(sr, 1050 "approx-older-change-not-synchronized-millis"))); 1051 } 1052 catch (Throwable t) 1053 { 1054 } 1055 try 1056 { 1057 replica.setMissingChanges( 1058 new Integer(getFirstValue(sr, "missing-changes"))); 1059 } 1060 catch (Throwable t) 1061 { 1062 } 1063 } 1064 } 1065 } 1066 } 1067 catch (NameNotFoundException nse) 1068 { 1069 } 1070 } 1071 } 1072 1073 boolean replicationSecure = false; 1074 if (replicationEnabled) 1075 { 1076 ctls = new SearchControls(); 1077 ctls.setSearchScope(SearchControls.OBJECT_SCOPE); 1078 ctls.setReturningAttributes( 1079 new String[] {"ds-cfg-ssl-encryption"}); 1080 filter = "(objectclass=ds-cfg-crypto-manager)"; 1081 1082 jndiName = new LdapName("cn=Crypto Manager,cn=config"); 1083 1084 NamingEnumeration entries = ctx.search(jndiName, filter, ctls); 1085 1086 while(entries.hasMore()) 1087 { 1088 SearchResult sr = (SearchResult)entries.next(); 1089 1090 String v = getFirstValue(sr, "ds-cfg-ssl-encryption"); 1091 replicationSecure = "true".equalsIgnoreCase(v); 1092 } 1093 } 1094 desc.serverProperties.put(ServerProperty.IS_REPLICATION_SECURE, 1095 replicationSecure ? Boolean.TRUE : Boolean.FALSE); 1096 } 1097 1098 /** 1099 Updates the instance key public-key certificate value of this context from 1100 the local truststore of the instance bound by this context. Any current 1101 value of the certificate is overwritten. The intent of this method is to 1102 retrieve the instance-key public-key certificate when this context is bound 1103 to an instance, and cache it for later use in registering the instance into 1104 ADS. 1105 @param desc The map to update with the instance key-pair public-key 1106 certificate. 1107 @param ctx The bound server instance. 1108 @throws NamingException if unable to retrieve certificate from bound 1109 instance. 1110 */ 1111 private static void updatePublicKeyCertificate(ServerDescriptor desc, 1112 InitialLdapContext ctx, TopologyCacheFilter filter) throws NamingException 1113 { 1114 /* TODO: this DN is declared in some core constants file. Create a constants 1115 file for the installer and import it into the core. */ 1116 final String dnStr = "ds-cfg-key-id=ads-certificate,cn=ads-truststore"; 1117 final LdapName dn = new LdapName(dnStr); 1118 for (int i = 0; i < 2 ; ++i) { 1119 /* If the entry does not exist in the instance's truststore backend, add 1120 it (which induces the CryptoManager to create the public-key 1121 certificate attribute), then repeat the search. */ 1122 try { 1123 final SearchControls searchControls = new SearchControls(); 1124 searchControls.setSearchScope(SearchControls.OBJECT_SCOPE); 1125 final String attrIDs[] = { "ds-cfg-public-key-certificate;binary" }; 1126 searchControls.setReturningAttributes(attrIDs); 1127 final SearchResult certEntry = ctx.search(dn, 1128 "(objectclass=ds-cfg-instance-key)", searchControls).next(); 1129 final Attribute certAttr = certEntry.getAttributes().get(attrIDs[0]); 1130 if (null != certAttr) { 1131 /* attribute ds-cfg-public-key-certificate is a MUST in the schema */ 1132 desc.serverProperties.put( 1133 ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE, 1134 certAttr.get()); 1135 } 1136 break; 1137 } 1138 catch (NameNotFoundException x) { 1139 if (0 == i) { 1140 /* Poke CryptoManager to initialize truststore. Note the special 1141 attribute in the request. */ 1142 final Attributes attrs = new BasicAttributes(); 1143 final Attribute oc = new BasicAttribute("objectclass"); 1144 oc.add("top"); 1145 oc.add("ds-cfg-self-signed-cert-request"); 1146 attrs.put(oc); 1147 ctx.createSubcontext(dn, attrs).close(); 1148 } 1149 else { 1150 throw x; 1151 } 1152 } 1153 } 1154 } 1155 1156 private static void updateMiscellaneous(ServerDescriptor desc, 1157 InitialLdapContext ctx, TopologyCacheFilter cacheFilter) 1158 throws NamingException 1159 { 1160 SearchControls ctls = new SearchControls(); 1161 ctls.setSearchScope(SearchControls.OBJECT_SCOPE); 1162 ctls.setReturningAttributes( 1163 new String[] { 1164 "ds-sync-generation-id" 1165 }); 1166 String filter = "|(objectclass=*)(objectclass=ldapsubentry)"; 1167 1168 LdapName jndiName = new LdapName("cn=schema"); 1169 NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); 1170 1171 while(listeners.hasMore()) 1172 { 1173 SearchResult sr = (SearchResult)listeners.next(); 1174 1175 desc.serverProperties.put(ServerProperty.SCHEMA_GENERATION_ID, 1176 getFirstValue(sr, "ds-sync-generation-id")); 1177 } 1178 } 1179 1180 /** 1181 Seeds the bound instance's local ads-truststore with a set of instance 1182 key-pair public key certificates. The result is the instance will trust any 1183 instance posessing the private key corresponding to one of the public-key 1184 certificates. This trust is necessary at least to initialize replication, 1185 which uses the trusted certificate entries in the ads-truststore for server 1186 authentication. 1187 @param ctx The bound instance. 1188 @param keyEntryMap The set of valid (i.e., not tagged as compromised) 1189 instance key-pair public-key certificate entries in ADS represented as a map 1190 from keyID to public-key certificate (binary). 1191 @throws NamingException in case an error occurs while updating the instance's 1192 ads-truststore via LDAP. 1193 */ 1194 public static void seedAdsTrustStore( 1195 InitialLdapContext ctx, 1196 Map<String, byte[]> keyEntryMap) 1197 throws NamingException 1198 { 1199 /* TODO: this DN is declared in some core constants file. Create a 1200 constants file for the installer and import it into the core. */ 1201 final String truststoreDnStr = "cn=ads-truststore"; 1202 final Attribute oc = new BasicAttribute("objectclass"); 1203 oc.add("top"); 1204 oc.add("ds-cfg-instance-key"); 1205 for (Map.Entry<String, byte[]> keyEntry : keyEntryMap.entrySet()){ 1206 final BasicAttributes keyAttrs = new BasicAttributes(); 1207 keyAttrs.put(oc); 1208 final Attribute rdnAttr = new BasicAttribute( 1209 ADSContext.ServerProperty.INSTANCE_KEY_ID.getAttributeName(), 1210 keyEntry.getKey()); 1211 keyAttrs.put(rdnAttr); 1212 keyAttrs.put(new BasicAttribute( 1213 ADSContext.ServerProperty.INSTANCE_PUBLIC_KEY_CERTIFICATE. 1214 getAttributeName() + ";binary", keyEntry.getValue())); 1215 final LdapName keyDn = new LdapName((new StringBuilder(rdnAttr.getID())) 1216 .append("=").append(Rdn.escapeValue(rdnAttr.get())).append(",") 1217 .append(truststoreDnStr).toString()); 1218 try { 1219 ctx.createSubcontext(keyDn, keyAttrs).close(); 1220 } 1221 catch(NameAlreadyBoundException x){ 1222 ctx.destroySubcontext(keyDn); 1223 ctx.createSubcontext(keyDn, keyAttrs).close(); 1224 } 1225 } 1226 } 1227 1228 /** 1229 * Returns the values of the ds-base-dn-entry count attributes for the given 1230 * backend monitor entry using the provided InitialLdapContext. 1231 * @param ctx the InitialLdapContext to use to update the configuration. 1232 * @param backendID the id of the backend. 1233 * @return the values of the ds-base-dn-entry count attribute. 1234 * @throws NamingException if there was an error. 1235 */ 1236 private static Set<String> getBaseDNEntryCount(InitialLdapContext ctx, 1237 String backendID) throws NamingException 1238 { 1239 LinkedHashSet<String> v = new LinkedHashSet<String>(); 1240 SearchControls ctls = new SearchControls(); 1241 ctls.setSearchScope(SearchControls.ONELEVEL_SCOPE); 1242 ctls.setReturningAttributes( 1243 new String[] { 1244 "ds-base-dn-entry-count" 1245 }); 1246 String filter = "(ds-backend-id="+backendID+")"; 1247 1248 LdapName jndiName = new LdapName("cn=monitor"); 1249 NamingEnumeration listeners = ctx.search(jndiName, filter, ctls); 1250 1251 while(listeners.hasMore()) 1252 { 1253 SearchResult sr = (SearchResult)listeners.next(); 1254 1255 v.addAll(getValues(sr, "ds-base-dn-entry-count")); 1256 } 1257 return v; 1258 } 1259 1260 /* 1261 * The following 2 methods are convenience methods to retrieve String values 1262 * from an entry. 1263 */ 1264 private static String getFirstValue(SearchResult entry, String attrName) 1265 throws NamingException 1266 { 1267 return ConnectionUtils.getFirstValue(entry, attrName); 1268 } 1269 1270 private static Set<String> getValues(SearchResult entry, String attrName) 1271 throws NamingException 1272 { 1273 return ConnectionUtils.getValues(entry, attrName); 1274 } 1275 1276 /** 1277 * An convenience method to know if the provided ID corresponds to a 1278 * configuration backend or not. 1279 * @param id the backend ID to analyze 1280 * @return <CODE>true</CODE> if the the id corresponds to a configuration 1281 * backend and <CODE>false</CODE> otherwise. 1282 */ 1283 private static boolean isConfigBackend(String id) 1284 { 1285 return "tasks".equalsIgnoreCase(id) || 1286 "schema".equalsIgnoreCase(id) || 1287 "config".equalsIgnoreCase(id) || 1288 "monitor".equalsIgnoreCase(id) || 1289 "backup".equalsIgnoreCase(id) || 1290 "ads-truststore".equalsIgnoreCase(id); 1291 } 1292 1293 /** 1294 * An convenience method to know if the provided ID corresponds to the schema 1295 * backend or not. 1296 * @param id the backend ID to analyze 1297 * @return <CODE>true</CODE> if the the id corresponds to the schema backend 1298 * and <CODE>false</CODE> otherwise. 1299 */ 1300 private static boolean isSchemaBackend(String id) 1301 { 1302 return "schema".equalsIgnoreCase(id); 1303 } 1304 /** 1305 * Returns <CODE>true</CODE> if the the provided strings represent the same 1306 * DN and <CODE>false</CODE> otherwise. 1307 * @param dn1 the first dn to compare. 1308 * @param dn2 the second dn to compare. 1309 * @return <CODE>true</CODE> if the the provided strings represent the same 1310 * DN and <CODE>false</CODE> otherwise. 1311 */ 1312 private static boolean areDnsEqual(String dn1, String dn2) 1313 { 1314 boolean areDnsEqual = false; 1315 try 1316 { 1317 LdapName name1 = new LdapName(dn1); 1318 LdapName name2 = new LdapName(dn2); 1319 areDnsEqual = name1.equals(name2); 1320 } catch (Exception ex) 1321 { 1322 /* ignore */ 1323 } 1324 return areDnsEqual; 1325 } 1326 }