001/* 002 * Copyright 2011-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2011-2019 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.listener; 022 023 024 025import java.io.File; 026import java.io.OutputStream; 027import java.io.Serializable; 028import java.net.Socket; 029import java.security.MessageDigest; 030import java.util.ArrayList; 031import java.util.EnumSet; 032import java.util.Iterator; 033import java.util.LinkedHashMap; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037import java.util.logging.FileHandler; 038import java.util.logging.Level; 039import java.util.logging.StreamHandler; 040import javax.net.ssl.KeyManager; 041import javax.net.ssl.TrustManager; 042 043import com.unboundid.ldap.sdk.DN; 044import com.unboundid.ldap.sdk.LDAPException; 045import com.unboundid.ldap.sdk.OperationType; 046import com.unboundid.ldap.sdk.ResultCode; 047import com.unboundid.ldap.sdk.Version; 048import com.unboundid.ldap.sdk.schema.Schema; 049import com.unboundid.util.CommandLineTool; 050import com.unboundid.util.Debug; 051import com.unboundid.util.MinimalLogFormatter; 052import com.unboundid.util.NotMutable; 053import com.unboundid.util.ObjectPair; 054import com.unboundid.util.StaticUtils; 055import com.unboundid.util.ThreadSafety; 056import com.unboundid.util.ThreadSafetyLevel; 057import com.unboundid.util.args.ArgumentException; 058import com.unboundid.util.args.ArgumentParser; 059import com.unboundid.util.args.BooleanArgument; 060import com.unboundid.util.args.DNArgument; 061import com.unboundid.util.args.IntegerArgument; 062import com.unboundid.util.args.FileArgument; 063import com.unboundid.util.args.StringArgument; 064import com.unboundid.util.ssl.KeyStoreKeyManager; 065import com.unboundid.util.ssl.SSLUtil; 066import com.unboundid.util.ssl.TrustAllTrustManager; 067import com.unboundid.util.ssl.TrustStoreTrustManager; 068import com.unboundid.util.ssl.cert.CertException; 069 070import static com.unboundid.ldap.listener.ListenerMessages.*; 071 072 073 074/** 075 * This class provides a command-line tool that can be used to run an instance 076 * of the in-memory directory server. Instances of the server may also be 077 * created and controlled programmatically using the 078 * {@link InMemoryDirectoryServer} class. 079 * <BR><BR> 080 * The following command-line arguments may be used with this class: 081 * <UL> 082 * <LI>"-b {baseDN}" or "--baseDN {baseDN}" -- specifies a base DN to use for 083 * the server. At least one base DN must be specified, and multiple 084 * base DNs may be provided as separate arguments.</LI> 085 * <LI>"-p {port}" or "--port {port}" -- specifies the port on which the 086 * server should listen for client connections. If this is not provided, 087 * then a free port will be automatically chosen for use by the 088 * server.</LI> 089 * <LI>"-l {path}" or "--ldifFile {path}" -- specifies the path to an LDIF 090 * file to use to initially populate the server. If this is not provided, 091 * then the server will initially be empty. The LDIF file will not be 092 * updated as operations are processed in the server.</LI> 093 * <LI>"-D {bindDN}" or "--additionalBindDN {bindDN}" -- specifies an 094 * additional DN that can be used to authenticate to the server, even if 095 * there is no account for that user. If this is provided, then the 096 * --additionalBindPassword argument must also be given.</LI> 097 * <LI>"-w {password}" or "--additionalBindPassword {password}" -- specifies 098 * the password that should be used when attempting to bind as the user 099 * specified with the "-additionalBindDN" argument. If this is provided, 100 * then the --additionalBindDN argument must also be given.</LI> 101 * <LI>"-c {count}" or "--maxChangeLogEntries {count}" -- Indicates whether an 102 * LDAP changelog should be enabled, and if so how many changelog records 103 * should be maintained. If this argument is not provided, or if it is 104 * provided with a value of zero, then no changelog will be 105 * maintained.</LI> 106 * <LI>"-A" or "--accessLogToStandardOut" -- indicates that access log 107 * information should be written to standard output. This cannot be 108 * provided in conjunction with the "--accessLogFile" argument. If 109 * that should be used as a server access log. This cannot be provided in 110 * neither argument is provided, then no access logging will be 111 * performed</LI> 112 * <LI>"-a {path}" or "--accessLogFile {path}" -- specifies the path to a file 113 * that should be used as a server access log. This cannot be provided in 114 * conjunction with the "--accessLogToStandardOut" argument. If neither 115 * argument is provided, then no access logging will be performed</LI> 116 * <LI>"--ldapDebugLogToStandardOut" -- Indicates that LDAP debug log 117 * information should be written to standard output. This cannot be 118 * provided in conjunction with the "--ldapDebugLogFile" argument. If 119 * neither argument is provided, then no debug logging will be 120 * performed.</LI> 121 * <LI>"-d {path}" or "--ldapDebugLogFile {path}" -- specifies the path to a 122 * file that should be used as a server LDAP debug log. This cannot be 123 * provided in conjunction with the "--ldapDebugLogToStandardOut" 124 * argument. If neither argument is provided, then no debug logging will 125 * be performed.</LI> 126 * <LI>"-s" or "--useDefaultSchema" -- Indicates that the server should use 127 * the default standard schema provided as part of the LDAP SDK. If 128 * neither this argument nor the "--useSchemaFile" argument is provided, 129 * then the server will not perform any schema validation.</LI> 130 * <LI>"-S {path}" or "--useSchemaFile {path}" -- specifies the path to a file 131 * or directory containing schema definitions to use for the server. If 132 * neither this argument nor the "--useDefaultSchema" argument is 133 * provided, then the server will not perform any schema validation. If 134 * the specified path represents a file, then it must be an LDIF file 135 * containing a valid LDAP subschema subentry. If the path is a 136 * directory, then its files will be processed in lexicographic order by 137 * name.</LI> 138 * <LI>"-I {attr}" or "--equalityIndex {attr}" -- specifies that an equality 139 * index should be maintained for the specified attribute. The equality 140 * index may be used to speed up certain kinds of searches, although it 141 * will cause the server to consume more memory.</LI> 142 * <LI>"-Z" or "--useSSL" -- indicates that the server should encrypt all 143 * communication using SSL. If this is provided, then the 144 * "--keyStorePath" and "--keyStorePassword" arguments must also be 145 * provided, and the "--useStartTLS" argument must not be provided.</LI> 146 * <LI>"-q" or "--useStartTLS" -- indicates that the server should support the 147 * use of the StartTLS extended request. If this is provided, then the 148 * "--keyStorePath" and "--keyStorePassword" arguments must also be 149 * provided, and the "--useSSL" argument must not be provided.</LI> 150 * <LI>"-K {path}" or "--keyStorePath {path}" -- specifies the path to the JKS 151 * key store file that should be used to obtain the server certificate to 152 * use for SSL communication. If this argument is provided, then the 153 * "--keyStorePassword" argument must also be provided, along with exactly 154 * one of the "--useSSL" or "--useStartTLS" arguments.</LI> 155 * <LI>"-W {password}" or "--keyStorePassword {password}" -- specifies the 156 * password that should be used to access the contents of the SSL key 157 * store. If this argument is provided, then the "--keyStorePath" 158 * argument must also be provided, along with exactly one of the 159 * "--useSSL" or "--useStartTLS" arguments.</LI> 160 * <LI>"--keyStoreType {type}" -- specifies the type of keystore represented 161 * by the file specified by the keystore path. If this argument is 162 * provided, then the "--keyStorePath" argument must also be provided, 163 * along with exactly one of the "--useSSL" or "--useStartTLS" arguments. 164 * If this argument is not provided, then a default key store type of 165 * "JKS" will be assumed.</LI> 166 * <LI>"--generateSelfSignedCertificate" -- indicates that the server should 167 * generate a self-signed certificate to use for SSL or StartTLS 168 * communication. If this argument is provided, then exactly one of the 169 * "--useSSL" or "--useStartTLS" arguments must also be specified.</LI> 170 * <LI>"-P {path}" or "--trustStorePath {path}" -- specifies the path to the 171 * JKS trust store file that should be used to determine whether to trust 172 * any SSL certificates that may be presented by the client. If this 173 * argument is provided, then exactly one of the "--useSSL" or 174 * "--useStartTLS" arguments must also be provided. If this argument is 175 * not provided but SSL or StartTLS is to be used, then all client 176 * certificates will be automatically trusted.</LI> 177 * <LI>"-T {password}" or "--trustStorePassword {password}" -- specifies the 178 * password that should be used to access the contents of the SSL trust 179 * store. If this argument is provided, then the "--trustStorePath" 180 * argument must also be provided, along with exactly one of the 181 * "--useSSL" or "--useStartTLS" arguments. If an SSL trust store path 182 * was provided without a trust store password, then the server will 183 * attempt to use the trust store without a password.</LI> 184 * <LI>"--trustStoreType {type}" -- specifies the type of trust store 185 * represented by the file specified by the trust store path. If this 186 * argument is provided, then the "--trustStorePath" argument must also 187 * be provided, along with exactly one of the "--useSSL" or 188 * "--useStartTLS" arguments. If this argument is not provided, then a 189 * default trust store type of "JKS" will be assumed.</LI> 190 * <LI>"--maxConcurrentConnections {num}" -- specifies the maximum number of 191 * concurrent connections that the server will allow.</LI> 192 * <LI>"--sizeLimit {num}" -- specifies the maximum number of entries that 193 * the server will reeturn for a single search operation.</LI> 194 * <LI>"--passwordAttribute {attr}" -- specifies an attribute that will hold 195 * user passwords.</LI> 196 * <LI>"--defaultPasswordEncoding {scheme}" -- specifies the name of the 197 * default scheme that the server will use to encode clear-text 198 * passwords. Allowed values include MD5, SMD5, SHA, SSHA, SHA256, 199 * SSHA256, SHA384, SSHA384, SHA512, SSHA512, CLEAR, BASE64, and HEX.</LI> 200 * <LI>"--allowedOperationType {type}" -- specifies a type of operation that 201 * the server will allow. Allowed values include add, bind, compare, 202 * delete, extended, modify, modify-dn, and search.</LI> 203 * <LI>"--authenticationRequiredOperationType {type}" -- specifies a type of 204 * operation that the server will only allow for authenticated clients. 205 * Allowed values include add, compare, delete, extended, modify, 206 * modify-dn, and search.</LI> 207 * <LI>"--vendorName {name}" -- specifies the vendor name value to appear in 208 * the server root DSE.</LI> 209 * <LI>"--vendorVersion {version}" -- specifies the vendor version value to 210 * appear in the server root DSE.</LI> 211 * </UL> 212 */ 213@NotMutable() 214@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 215public final class InMemoryDirectoryServerTool 216 extends CommandLineTool 217 implements Serializable, LDAPListenerExceptionHandler 218{ 219 /** 220 * The serial version UID for this serializable class. 221 */ 222 private static final long serialVersionUID = 6484637038039050412L; 223 224 225 226 // The argument used to indicate that access log information should be written 227 // to standard output. 228 private BooleanArgument accessLogToStandardOutArgument; 229 230 // The argument used to prevent the in-memory server from starting. This is 231 // only intended to be used for internal testing purposes. 232 private BooleanArgument dontStartArgument; 233 234 // The argument used to indicate that the server should generate a self-signed 235 // certificate for use in SSL or StartTLS negotiation. 236 private BooleanArgument generateSelfSignedCertificateArgument; 237 238 // The argument used to indicate that LDAP debug log information should be 239 // written to standard output. 240 private BooleanArgument ldapDebugLogToStandardOutArgument; 241 242 // The argument used to indicate that the default standard schema should be 243 // used. 244 private BooleanArgument useDefaultSchemaArgument; 245 246 // The argument used to indicate that the server should use SSL 247 private BooleanArgument useSSLArgument; 248 249 // The argument used to indicate that the server should support the StartTLS 250 // extended operation 251 private BooleanArgument useStartTLSArgument; 252 253 // The argument used to specify an additional bind DN to use for the server. 254 private DNArgument additionalBindDNArgument; 255 256 // The argument used to specify the base DNs to use for the server. 257 private DNArgument baseDNArgument; 258 259 // The argument used to specify the path to an access log file to which 260 // information should be written about operations processed by the server. 261 private FileArgument accessLogFileArgument; 262 263 // The argument used to specify the code log file to use, if any. 264 private FileArgument codeLogFile; 265 266 // The argument used to specify the path to the SSL key store file. 267 private FileArgument keyStorePathArgument; 268 269 // The argument used to specify the path to an LDAP debug log file to which 270 // information should be written about detailed LDAP communication performed 271 // by the server. 272 private FileArgument ldapDebugLogFileArgument; 273 274 // The argument used to specify the path to an LDIF file with data to use to 275 // initially populate the server. 276 private FileArgument ldifFileArgument; 277 278 // The argument used to specify the path to the SSL trust store file. 279 private FileArgument trustStorePathArgument; 280 281 // The argument used to specify the path to a directory containing schema 282 // definitions. 283 private FileArgument useSchemaFileArgument; 284 285 // The in-memory directory server instance that has been created by this tool. 286 private InMemoryDirectoryServer directoryServer; 287 288 // The argument used to specify the maximum number of changelog entries that 289 // the server should maintain. 290 private IntegerArgument maxChangeLogEntriesArgument; 291 292 // The argument used to specify the maximum number of concurrent connections. 293 private IntegerArgument maxConcurrentConnectionsArgument; 294 295 // The argument used to specify the port on which the server should listen. 296 private IntegerArgument portArgument; 297 298 // The argument used to specify the maximum search size limit. 299 private IntegerArgument sizeLimitArgument; 300 301 // The argument used to specify the password for the additional bind DN. 302 private StringArgument additionalBindPasswordArgument; 303 304 // The argument used to specify the types of allowed operations. 305 private StringArgument allowedOperationTypeArgument; 306 307 // The argument used to specify the types of operations for which 308 // authentication is required. 309 private StringArgument authenticationRequiredOperationTypeArgument; 310 311 // The argument used to specify the name of the default encoding scheme to use 312 // use for clear-text passwords. 313 private StringArgument defaultPasswordEncodingArgument; 314 315 // The argument used to specify the attributes for which to maintain equality 316 // indexes. 317 private StringArgument equalityIndexArgument; 318 319 // The argument used to specify the password to use to access the contents of 320 // the SSL key store 321 private StringArgument keyStorePasswordArgument; 322 323 // The argument used to specify the key store type. 324 private StringArgument keyStoreTypeArgument; 325 326 // The argument used to specify the password attribute types. 327 private StringArgument passwordAttributeArgument; 328 329 // The argument used to specify the password to use to access the contents of 330 // the SSL trust store 331 private StringArgument trustStorePasswordArgument; 332 333 // The argument used to specify the trust store type. 334 private StringArgument trustStoreTypeArgument; 335 336 // The argument used to specify the server vendor name. 337 private StringArgument vendorNameArgument; 338 339 // The argument used to specify the server vendor version. 340 private StringArgument vendorVersionArgument; 341 342 343 344 /** 345 * Parse the provided command line arguments and uses them to start the 346 * directory server. 347 * 348 * @param args The command line arguments provided to this program. 349 */ 350 public static void main(final String... args) 351 { 352 final ResultCode resultCode = main(args, System.out, System.err); 353 if (resultCode != ResultCode.SUCCESS) 354 { 355 System.exit(resultCode.intValue()); 356 } 357 } 358 359 360 361 /** 362 * Parse the provided command line arguments and uses them to start the 363 * directory server. 364 * 365 * @param outStream The output stream to which standard out should be 366 * written. It may be {@code null} if output should be 367 * suppressed. 368 * @param errStream The output stream to which standard error should be 369 * written. It may be {@code null} if error messages 370 * should be suppressed. 371 * @param args The command line arguments provided to this program. 372 * 373 * @return A result code indicating whether the processing was successful. 374 */ 375 public static ResultCode main(final String[] args, 376 final OutputStream outStream, 377 final OutputStream errStream) 378 { 379 final InMemoryDirectoryServerTool tool = 380 new InMemoryDirectoryServerTool(outStream, errStream); 381 return tool.runTool(args); 382 } 383 384 385 386 /** 387 * Creates a new instance of this tool that use the provided output streams 388 * for standard output and standard error. 389 * 390 * @param outStream The output stream to use for standard output. It may be 391 * {@code System.out} for the JVM's default standard output 392 * stream, {@code null} if no output should be generated, 393 * or a custom output stream if the output should be sent 394 * to an alternate location. 395 * @param errStream The output stream to use for standard error. It may be 396 * {@code System.err} for the JVM's default standard error 397 * stream, {@code null} if no output should be generated, 398 * or a custom output stream if the output should be sent 399 * to an alternate location. 400 */ 401 public InMemoryDirectoryServerTool(final OutputStream outStream, 402 final OutputStream errStream) 403 { 404 super(outStream, errStream); 405 406 directoryServer = null; 407 dontStartArgument = null; 408 generateSelfSignedCertificateArgument = null; 409 useDefaultSchemaArgument = null; 410 useSSLArgument = null; 411 useStartTLSArgument = null; 412 additionalBindDNArgument = null; 413 baseDNArgument = null; 414 accessLogToStandardOutArgument = null; 415 accessLogFileArgument = null; 416 keyStorePathArgument = null; 417 ldapDebugLogToStandardOutArgument = null; 418 ldapDebugLogFileArgument = null; 419 ldifFileArgument = null; 420 trustStorePathArgument = null; 421 useSchemaFileArgument = null; 422 maxChangeLogEntriesArgument = null; 423 maxConcurrentConnectionsArgument = null; 424 portArgument = null; 425 sizeLimitArgument = null; 426 additionalBindPasswordArgument = null; 427 allowedOperationTypeArgument = null; 428 authenticationRequiredOperationTypeArgument = null; 429 defaultPasswordEncodingArgument = null; 430 equalityIndexArgument = null; 431 keyStorePasswordArgument = null; 432 keyStoreTypeArgument = null; 433 passwordAttributeArgument = null; 434 trustStorePasswordArgument = null; 435 trustStoreTypeArgument = null; 436 vendorNameArgument = null; 437 vendorVersionArgument = null; 438 } 439 440 441 442 /** 443 * {@inheritDoc} 444 */ 445 @Override() 446 public String getToolName() 447 { 448 return "in-memory-directory-server"; 449 } 450 451 452 453 /** 454 * {@inheritDoc} 455 */ 456 @Override() 457 public String getToolDescription() 458 { 459 return INFO_MEM_DS_TOOL_DESC.get(InMemoryDirectoryServer.class.getName()); 460 } 461 462 463 464 /** 465 * Retrieves the version string for this tool. 466 * 467 * @return The version string for this tool. 468 */ 469 @Override() 470 public String getToolVersion() 471 { 472 return Version.NUMERIC_VERSION_STRING; 473 } 474 475 476 477 /** 478 * {@inheritDoc} 479 */ 480 @Override() 481 public void addToolArguments(final ArgumentParser parser) 482 throws ArgumentException 483 { 484 portArgument = new IntegerArgument('p', "port", false, 1, 485 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PORT.get(), 486 INFO_MEM_DS_TOOL_ARG_DESC_PORT.get(), 0, 65_535); 487 portArgument.setArgumentGroupName( 488 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 489 parser.addArgument(portArgument); 490 491 useSSLArgument = new BooleanArgument('Z', "useSSL", 492 INFO_MEM_DS_TOOL_ARG_DESC_USE_SSL.get()); 493 useSSLArgument.setArgumentGroupName( 494 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 495 useSSLArgument.addLongIdentifier("use-ssl", true); 496 parser.addArgument(useSSLArgument); 497 498 useStartTLSArgument = new BooleanArgument('q', "useStartTLS", 499 INFO_MEM_DS_TOOL_ARG_DESC_USE_START_TLS.get()); 500 useStartTLSArgument.setArgumentGroupName( 501 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 502 useStartTLSArgument.addLongIdentifier("use-starttls", true); 503 useStartTLSArgument.addLongIdentifier("use-start-tls", true); 504 parser.addArgument(useStartTLSArgument); 505 506 keyStorePathArgument = new FileArgument('K', "keyStorePath", false, 1, 507 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 508 INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_PATH.get(), true, true, true, 509 false); 510 keyStorePathArgument.setArgumentGroupName( 511 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 512 keyStorePathArgument.addLongIdentifier("key-store-path", true); 513 parser.addArgument(keyStorePathArgument); 514 515 keyStorePasswordArgument = new StringArgument('W', "keyStorePassword", 516 false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(), 517 INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_PW.get()); 518 keyStorePasswordArgument.setSensitive(true); 519 keyStorePasswordArgument.setArgumentGroupName( 520 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 521 keyStorePasswordArgument.addLongIdentifier("keyStorePIN", true); 522 keyStorePasswordArgument.addLongIdentifier("key-store-password", true); 523 keyStorePasswordArgument.addLongIdentifier("key-store-pin", true); 524 parser.addArgument(keyStorePasswordArgument); 525 526 keyStoreTypeArgument = new StringArgument(null, "keyStoreType", 527 false, 1, "{type}", INFO_MEM_DS_TOOL_ARG_DESC_KEY_STORE_TYPE.get(), 528 "JKS"); 529 keyStoreTypeArgument.setArgumentGroupName( 530 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 531 keyStoreTypeArgument.addLongIdentifier("keyStoreFormat", true); 532 keyStoreTypeArgument.addLongIdentifier("key-store-type", true); 533 keyStoreTypeArgument.addLongIdentifier("key-store-format", true); 534 parser.addArgument(keyStoreTypeArgument); 535 536 generateSelfSignedCertificateArgument = new BooleanArgument(null, 537 "generateSelfSignedCertificate", 1, 538 INFO_MEM_DS_TOOL_ARG_DESC_SELF_SIGNED_CERT.get()); 539 generateSelfSignedCertificateArgument.setArgumentGroupName( 540 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 541 generateSelfSignedCertificateArgument.addLongIdentifier( 542 "useSelfSignedCertificate", true); 543 generateSelfSignedCertificateArgument.addLongIdentifier( 544 "selfSignedCertificate", true); 545 generateSelfSignedCertificateArgument.addLongIdentifier( 546 "generate-self-signed-certificate", true); 547 generateSelfSignedCertificateArgument.addLongIdentifier( 548 "use-self-signed-certificate", true); 549 generateSelfSignedCertificateArgument.addLongIdentifier( 550 "self-signed-certificate", true); 551 parser.addArgument(generateSelfSignedCertificateArgument); 552 553 trustStorePathArgument = new FileArgument('P', "trustStorePath", false, 1, 554 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 555 INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_PATH.get(), true, true, true, 556 false); 557 trustStorePathArgument.setArgumentGroupName( 558 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 559 trustStorePathArgument.addLongIdentifier("trust-store-path", true); 560 parser.addArgument(trustStorePathArgument); 561 562 trustStorePasswordArgument = new StringArgument('T', "trustStorePassword", 563 false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(), 564 INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_PW.get()); 565 trustStorePasswordArgument.setSensitive(true); 566 trustStorePasswordArgument.setArgumentGroupName( 567 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 568 trustStorePasswordArgument.addLongIdentifier("trustStorePIN", true); 569 trustStorePasswordArgument.addLongIdentifier("trust-store-password", true); 570 trustStorePasswordArgument.addLongIdentifier("trust-store-pin", true); 571 parser.addArgument(trustStorePasswordArgument); 572 573 trustStoreTypeArgument = new StringArgument(null, "trustStoreType", 574 false, 1, "{type}", INFO_MEM_DS_TOOL_ARG_DESC_TRUST_STORE_TYPE.get(), 575 "JKS"); 576 trustStoreTypeArgument.setArgumentGroupName( 577 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 578 trustStoreTypeArgument.addLongIdentifier("trustStoreFormat", true); 579 trustStoreTypeArgument.addLongIdentifier("trust-store-type", true); 580 trustStoreTypeArgument.addLongIdentifier("trust-store-format", true); 581 parser.addArgument(trustStoreTypeArgument); 582 583 maxConcurrentConnectionsArgument = new IntegerArgument(null, 584 "maxConcurrentConnections", false, 1, null, 585 INFO_MEM_DS_TOOL_ARG_DESC_MAX_CONNECTIONS.get(), 1, 586 Integer.MAX_VALUE, Integer.MAX_VALUE); 587 maxConcurrentConnectionsArgument.setArgumentGroupName( 588 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 589 maxConcurrentConnectionsArgument.addLongIdentifier( 590 "maximumConcurrentConnections", true); 591 maxConcurrentConnectionsArgument.addLongIdentifier( 592 "maxConnections", true); 593 maxConcurrentConnectionsArgument.addLongIdentifier( 594 "maximumConnections", true); 595 maxConcurrentConnectionsArgument.addLongIdentifier( 596 "max-concurrent-connections", true); 597 maxConcurrentConnectionsArgument.addLongIdentifier( 598 "maximum-concurrent-connections", true); 599 maxConcurrentConnectionsArgument.addLongIdentifier( 600 "max-connections", true); 601 maxConcurrentConnectionsArgument.addLongIdentifier( 602 "maximum-connections", true); 603 parser.addArgument(maxConcurrentConnectionsArgument); 604 605 dontStartArgument = new BooleanArgument(null, "dontStart", 606 INFO_MEM_DS_TOOL_ARG_DESC_DONT_START.get()); 607 dontStartArgument.setArgumentGroupName( 608 INFO_MEM_DS_TOOL_GROUP_CONNECTIVITY.get()); 609 dontStartArgument.setHidden(true); 610 dontStartArgument.addLongIdentifier("doNotStart", true); 611 dontStartArgument.addLongIdentifier("dont-start", true); 612 dontStartArgument.addLongIdentifier("do-not-start", true); 613 parser.addArgument(dontStartArgument); 614 615 baseDNArgument = new DNArgument('b', "baseDN", true, 0, 616 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_BASE_DN.get(), 617 INFO_MEM_DS_TOOL_ARG_DESC_BASE_DN.get()); 618 baseDNArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get()); 619 baseDNArgument.addLongIdentifier("base-dn", true); 620 parser.addArgument(baseDNArgument); 621 622 ldifFileArgument = new FileArgument('l', "ldifFile", false, 1, 623 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 624 INFO_MEM_DS_TOOL_ARG_DESC_LDIF_FILE.get(), true, true, true, false); 625 ldifFileArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get()); 626 ldifFileArgument.addLongIdentifier("ldif-file", true); 627 parser.addArgument(ldifFileArgument); 628 629 additionalBindDNArgument = new DNArgument('D', "additionalBindDN", false, 1, 630 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_BIND_DN.get(), 631 INFO_MEM_DS_TOOL_ARG_DESC_ADDITIONAL_BIND_DN.get()); 632 additionalBindDNArgument.setArgumentGroupName( 633 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 634 additionalBindDNArgument.addLongIdentifier("additional-bind-dn", true); 635 parser.addArgument(additionalBindDNArgument); 636 637 additionalBindPasswordArgument = new StringArgument('w', 638 "additionalBindPassword", false, 1, 639 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PASSWORD.get(), 640 INFO_MEM_DS_TOOL_ARG_DESC_ADDITIONAL_BIND_PW.get()); 641 additionalBindPasswordArgument.setSensitive(true); 642 additionalBindPasswordArgument.setArgumentGroupName( 643 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 644 additionalBindPasswordArgument.addLongIdentifier( 645 "additional-bind-password", true); 646 parser.addArgument(additionalBindPasswordArgument); 647 648 useDefaultSchemaArgument = new BooleanArgument('s', "useDefaultSchema", 649 INFO_MEM_DS_TOOL_ARG_DESC_USE_DEFAULT_SCHEMA.get()); 650 useDefaultSchemaArgument.setArgumentGroupName( 651 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 652 useDefaultSchemaArgument.addLongIdentifier("use-default-schema", true); 653 parser.addArgument(useDefaultSchemaArgument); 654 655 useSchemaFileArgument = new FileArgument('S', "useSchemaFile", false, 0, 656 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 657 INFO_MEM_DS_TOOL_ARG_DESC_USE_SCHEMA_FILE.get(), true, true, false, 658 false); 659 useSchemaFileArgument.setArgumentGroupName( 660 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 661 useSchemaFileArgument.addLongIdentifier("use-schema-file", true); 662 parser.addArgument(useSchemaFileArgument); 663 664 equalityIndexArgument = new StringArgument('I', "equalityIndex", false, 0, 665 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_ATTR.get(), 666 INFO_MEM_DS_TOOL_ARG_DESC_EQ_INDEX.get()); 667 equalityIndexArgument.setArgumentGroupName( 668 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 669 equalityIndexArgument.addLongIdentifier("equality-index", true); 670 parser.addArgument(equalityIndexArgument); 671 672 maxChangeLogEntriesArgument = new IntegerArgument('c', 673 "maxChangeLogEntries", false, 1, 674 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_COUNT.get(), 675 INFO_MEM_DS_TOOL_ARG_DESC_MAX_CHANGELOG_ENTRIES.get(), 0, 676 Integer.MAX_VALUE, 0); 677 maxChangeLogEntriesArgument.setArgumentGroupName( 678 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 679 maxChangeLogEntriesArgument.addLongIdentifier("max-changelog-entries", 680 true); 681 maxChangeLogEntriesArgument.addLongIdentifier("max-change-log-entries", 682 true); 683 parser.addArgument(maxChangeLogEntriesArgument); 684 685 sizeLimitArgument = new IntegerArgument(null, "sizeLimit", false, 1, null, 686 INFO_MEM_DS_TOOL_ARG_DESC_SIZE_LIMIT.get(), 1, Integer.MAX_VALUE, 687 Integer.MAX_VALUE); 688 sizeLimitArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get()); 689 sizeLimitArgument.addLongIdentifier("searchSizeLimit", true); 690 sizeLimitArgument.addLongIdentifier("size-limit", true); 691 sizeLimitArgument.addLongIdentifier("search-size-limit", true); 692 parser.addArgument(sizeLimitArgument); 693 694 passwordAttributeArgument = new StringArgument(null, "passwordAttribute", 695 false, 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_ATTR.get(), 696 INFO_MEM_DS_TOOL_ARG_DESC_PASSWORD_ATTRIBUTE.get(), "userPassword"); 697 passwordAttributeArgument.setArgumentGroupName( 698 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 699 passwordAttributeArgument.addLongIdentifier("passwordAttributeType", true); 700 passwordAttributeArgument.addLongIdentifier("password-attribute", true); 701 passwordAttributeArgument.addLongIdentifier("password-attribute-type", 702 true); 703 parser.addArgument(passwordAttributeArgument); 704 705 final Set<String> allowedSchemes = StaticUtils.setOf("md5", "smd5", "sha", 706 "ssha", "sha256", "ssha256", "sha384", "ssha384", "sha512", "ssha512", 707 "clear", "base64", "hex"); 708 defaultPasswordEncodingArgument = new StringArgument(null, 709 "defaultPasswordEncoding", false, 1, 710 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_SCHEME.get(), 711 INFO_MEM_DS_TOOL_ARG_DESC_DEFAULT_PASSWORD_ENCODING.get(), 712 allowedSchemes); 713 defaultPasswordEncodingArgument.setArgumentGroupName( 714 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 715 defaultPasswordEncodingArgument.addLongIdentifier( 716 "defaultPasswordEncodingScheme", true); 717 defaultPasswordEncodingArgument.addLongIdentifier( 718 "defaultPasswordStorageScheme", true); 719 defaultPasswordEncodingArgument.addLongIdentifier( 720 "defaultPasswordScheme", true); 721 defaultPasswordEncodingArgument.addLongIdentifier( 722 "default-password-encoding", true); 723 defaultPasswordEncodingArgument.addLongIdentifier( 724 "default-password-encoding-scheme", true); 725 defaultPasswordEncodingArgument.addLongIdentifier( 726 "default-password-storage-scheme", true); 727 defaultPasswordEncodingArgument.addLongIdentifier( 728 "default-password-scheme", true); 729 parser.addArgument(defaultPasswordEncodingArgument); 730 731 final Set<String> allowedOperationTypeAllowedValues = StaticUtils.setOf( 732 "add", "bind", "compare", "delete", "extended", "modify", "modify-dn", 733 "search"); 734 allowedOperationTypeArgument = new StringArgument(null, 735 "allowedOperationType", false, 0, 736 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_TYPE.get(), 737 INFO_MEM_DS_TOOL_ARG_DESC_ALLOWED_OP_TYPE.get(), 738 allowedOperationTypeAllowedValues); 739 allowedOperationTypeArgument.setArgumentGroupName( 740 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 741 allowedOperationTypeArgument.addLongIdentifier("allowed-operation-type", 742 true); 743 parser.addArgument(allowedOperationTypeArgument); 744 745 final Set<String> authRequiredTypeAllowedValues = StaticUtils.setOf("add", 746 "compare", "delete", "extended", "modify", "modify-dn", "search"); 747 authenticationRequiredOperationTypeArgument = new StringArgument(null, 748 "authenticationRequiredOperationType", false, 0, 749 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_TYPE.get(), 750 INFO_MEM_DS_TOOL_ARG_DESC_AUTH_REQUIRED_OP_TYPE.get(), 751 authRequiredTypeAllowedValues); 752 authenticationRequiredOperationTypeArgument.setArgumentGroupName( 753 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 754 authenticationRequiredOperationTypeArgument.addLongIdentifier( 755 "requiredAuthenticationOperationType", true); 756 authenticationRequiredOperationTypeArgument.addLongIdentifier( 757 "requireAuthenticationOperationType", true); 758 authenticationRequiredOperationTypeArgument.addLongIdentifier( 759 "authentication-required-operation-type", true); 760 authenticationRequiredOperationTypeArgument.addLongIdentifier( 761 "required-authentication-operation-type", true); 762 authenticationRequiredOperationTypeArgument.addLongIdentifier( 763 "require-authentication-operation-type", true); 764 parser.addArgument(authenticationRequiredOperationTypeArgument); 765 766 vendorNameArgument = new StringArgument(null, "vendorName", false, 1, 767 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_VALUE.get(), 768 INFO_MEM_DS_TOOL_ARG_DESC_VENDOR_NAME.get()); 769 vendorNameArgument.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_DATA.get()); 770 vendorNameArgument.addLongIdentifier("vendor-name", true); 771 parser.addArgument(vendorNameArgument); 772 773 vendorVersionArgument = new StringArgument(null, "vendorVersion", false, 1, 774 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_VALUE.get(), 775 INFO_MEM_DS_TOOL_ARG_DESC_VENDOR_VERSION.get()); 776 vendorVersionArgument.setArgumentGroupName( 777 INFO_MEM_DS_TOOL_GROUP_DATA.get()); 778 vendorVersionArgument.addLongIdentifier("vendor-version", true); 779 parser.addArgument(vendorVersionArgument); 780 781 accessLogToStandardOutArgument = new BooleanArgument('A', 782 "accessLogToStandardOut", 783 INFO_MEM_DS_TOOL_ARG_DESC_ACCESS_LOG_TO_STDOUT.get()); 784 accessLogToStandardOutArgument.setArgumentGroupName( 785 INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 786 accessLogToStandardOutArgument.addLongIdentifier( 787 "access-log-to-standard-out", true); 788 parser.addArgument(accessLogToStandardOutArgument); 789 790 accessLogFileArgument = new FileArgument('a', "accessLogFile", false, 1, 791 INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 792 INFO_MEM_DS_TOOL_ARG_DESC_ACCESS_LOG_FILE.get(), false, true, true, 793 false); 794 accessLogFileArgument.setArgumentGroupName( 795 INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 796 accessLogFileArgument.addLongIdentifier("access-log-format", true); 797 parser.addArgument(accessLogFileArgument); 798 799 ldapDebugLogToStandardOutArgument = new BooleanArgument(null, 800 "ldapDebugLogToStandardOut", 801 INFO_MEM_DS_TOOL_ARG_DESC_LDAP_DEBUG_LOG_TO_STDOUT.get()); 802 ldapDebugLogToStandardOutArgument.setArgumentGroupName( 803 INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 804 ldapDebugLogToStandardOutArgument.addLongIdentifier( 805 "ldap-debug-log-to-standard-out", true); 806 parser.addArgument(ldapDebugLogToStandardOutArgument); 807 808 ldapDebugLogFileArgument = new FileArgument('d', "ldapDebugLogFile", false, 809 1, INFO_MEM_DS_TOOL_ARG_PLACEHOLDER_PATH.get(), 810 INFO_MEM_DS_TOOL_ARG_DESC_LDAP_DEBUG_LOG_FILE.get(), false, true, true, 811 false); 812 ldapDebugLogFileArgument.setArgumentGroupName( 813 INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 814 ldapDebugLogFileArgument.addLongIdentifier("ldap-debug-log-file", true); 815 parser.addArgument(ldapDebugLogFileArgument); 816 817 codeLogFile = new FileArgument('C', "codeLogFile", false, 1, "{path}", 818 INFO_MEM_DS_TOOL_ARG_DESC_CODE_LOG_FILE.get(), false, true, true, 819 false); 820 codeLogFile.setArgumentGroupName(INFO_MEM_DS_TOOL_GROUP_LOGGING.get()); 821 codeLogFile.addLongIdentifier("code-log-file", true); 822 parser.addArgument(codeLogFile); 823 824 parser.addExclusiveArgumentSet(useDefaultSchemaArgument, 825 useSchemaFileArgument); 826 827 parser.addExclusiveArgumentSet(useSSLArgument, useStartTLSArgument); 828 829 parser.addExclusiveArgumentSet(keyStorePathArgument, 830 generateSelfSignedCertificateArgument); 831 832 parser.addExclusiveArgumentSet(accessLogToStandardOutArgument, 833 accessLogFileArgument); 834 835 parser.addExclusiveArgumentSet(ldapDebugLogToStandardOutArgument, 836 ldapDebugLogFileArgument); 837 838 parser.addDependentArgumentSet(additionalBindDNArgument, 839 additionalBindPasswordArgument); 840 841 parser.addDependentArgumentSet(additionalBindPasswordArgument, 842 additionalBindDNArgument); 843 844 parser.addDependentArgumentSet(useSSLArgument, keyStorePathArgument, 845 generateSelfSignedCertificateArgument); 846 847 parser.addDependentArgumentSet(keyStorePathArgument, 848 keyStorePasswordArgument); 849 850 parser.addDependentArgumentSet(keyStorePasswordArgument, 851 keyStorePathArgument); 852 853 parser.addDependentArgumentSet(keyStoreTypeArgument, 854 keyStorePathArgument); 855 856 parser.addDependentArgumentSet(useStartTLSArgument, keyStorePathArgument, 857 generateSelfSignedCertificateArgument); 858 859 parser.addDependentArgumentSet(keyStorePathArgument, useSSLArgument, 860 useStartTLSArgument); 861 862 parser.addDependentArgumentSet(generateSelfSignedCertificateArgument, 863 useSSLArgument, useStartTLSArgument); 864 865 parser.addDependentArgumentSet(trustStorePathArgument, useSSLArgument, 866 useStartTLSArgument); 867 868 parser.addDependentArgumentSet(trustStorePasswordArgument, 869 trustStorePathArgument); 870 871 parser.addDependentArgumentSet(trustStoreTypeArgument, 872 trustStorePathArgument); 873 } 874 875 876 877 /** 878 * {@inheritDoc} 879 */ 880 @Override() 881 public boolean supportsInteractiveMode() 882 { 883 return true; 884 } 885 886 887 888 /** 889 * {@inheritDoc} 890 */ 891 @Override() 892 public boolean defaultsToInteractiveMode() 893 { 894 return true; 895 } 896 897 898 899 /** 900 * Indicates whether this tool supports the use of a properties file for 901 * specifying default values for arguments that aren't specified on the 902 * command line. 903 * 904 * @return {@code true} if this tool supports the use of a properties file 905 * for specifying default values for arguments that aren't specified 906 * on the command line, or {@code false} if not. 907 */ 908 @Override() 909 public boolean supportsPropertiesFile() 910 { 911 return true; 912 } 913 914 915 916 /** 917 * {@inheritDoc} 918 */ 919 @Override() 920 public ResultCode doToolProcessing() 921 { 922 // Create a base configuration. 923 final InMemoryDirectoryServerConfig serverConfig; 924 try 925 { 926 serverConfig = getConfig(); 927 } 928 catch (final LDAPException le) 929 { 930 Debug.debugException(le); 931 err(ERR_MEM_DS_TOOL_ERROR_INITIALIZING_CONFIG.get(le.getMessage())); 932 return le.getResultCode(); 933 } 934 935 936 // Create the server instance using the provided configuration, but don't 937 // start it yet. 938 try 939 { 940 directoryServer = new InMemoryDirectoryServer(serverConfig); 941 } 942 catch (final LDAPException le) 943 { 944 Debug.debugException(le); 945 err(ERR_MEM_DS_TOOL_ERROR_CREATING_SERVER_INSTANCE.get(le.getMessage())); 946 return le.getResultCode(); 947 } 948 949 950 // If an LDIF file was provided, then use it to populate the server. 951 if (ldifFileArgument.isPresent()) 952 { 953 final File ldifFile = ldifFileArgument.getValue(); 954 try 955 { 956 final int numEntries = directoryServer.importFromLDIF(true, 957 ldifFile.getAbsolutePath()); 958 out(INFO_MEM_DS_TOOL_ADDED_ENTRIES_FROM_LDIF.get(numEntries, 959 ldifFile.getAbsolutePath())); 960 } 961 catch (final LDAPException le) 962 { 963 Debug.debugException(le); 964 err(ERR_MEM_DS_TOOL_ERROR_POPULATING_SERVER_INSTANCE.get( 965 ldifFile.getAbsolutePath(), le.getMessage())); 966 return le.getResultCode(); 967 } 968 } 969 970 971 // Start the server. 972 try 973 { 974 if (! dontStartArgument.isPresent()) 975 { 976 directoryServer.startListening(); 977 out(INFO_MEM_DS_TOOL_LISTENING.get(directoryServer.getListenPort())); 978 } 979 } 980 catch (final Exception e) 981 { 982 Debug.debugException(e); 983 err(ERR_MEM_DS_TOOL_ERROR_STARTING_SERVER.get( 984 StaticUtils.getExceptionMessage(e))); 985 return ResultCode.LOCAL_ERROR; 986 } 987 988 return ResultCode.SUCCESS; 989 } 990 991 992 993 /** 994 * Creates a server configuration based on information provided with 995 * command line arguments. 996 * 997 * @return The configuration that was created. 998 * 999 * @throws LDAPException If a problem is encountered while creating the 1000 * configuration. 1001 */ 1002 private InMemoryDirectoryServerConfig getConfig() 1003 throws LDAPException 1004 { 1005 final List<DN> dnList = baseDNArgument.getValues(); 1006 final DN[] baseDNs = new DN[dnList.size()]; 1007 dnList.toArray(baseDNs); 1008 1009 final InMemoryDirectoryServerConfig serverConfig = 1010 new InMemoryDirectoryServerConfig(baseDNs); 1011 1012 1013 // If a listen port was specified, then update the configuration to use it. 1014 int listenPort = 0; 1015 if (portArgument.isPresent()) 1016 { 1017 listenPort = portArgument.getValue(); 1018 } 1019 1020 1021 // If schema should be used, then get it. 1022 if (useDefaultSchemaArgument.isPresent()) 1023 { 1024 serverConfig.setSchema(Schema.getDefaultStandardSchema()); 1025 } 1026 else if (useSchemaFileArgument.isPresent()) 1027 { 1028 final ArrayList<File> schemaFiles = new ArrayList<>(10); 1029 for (final File f : useSchemaFileArgument.getValues()) 1030 { 1031 if (f.exists()) 1032 { 1033 if (f.isFile()) 1034 { 1035 schemaFiles.add(f); 1036 } 1037 else 1038 { 1039 for (final File subFile : f.listFiles()) 1040 { 1041 if (subFile.isFile()) 1042 { 1043 schemaFiles.add(subFile); 1044 } 1045 } 1046 } 1047 } 1048 else 1049 { 1050 throw new LDAPException(ResultCode.PARAM_ERROR, 1051 ERR_MEM_DS_TOOL_NO_SUCH_SCHEMA_FILE.get(f.getAbsolutePath())); 1052 } 1053 } 1054 1055 try 1056 { 1057 serverConfig.setSchema(Schema.getSchema(schemaFiles)); 1058 } 1059 catch (final Exception e) 1060 { 1061 Debug.debugException(e); 1062 1063 final StringBuilder fileList = new StringBuilder(); 1064 final Iterator<File> fileIterator = schemaFiles.iterator(); 1065 while (fileIterator.hasNext()) 1066 { 1067 fileList.append(fileIterator.next().getAbsolutePath()); 1068 if (fileIterator.hasNext()) 1069 { 1070 fileList.append(", "); 1071 } 1072 } 1073 1074 throw new LDAPException(ResultCode.LOCAL_ERROR, 1075 ERR_MEM_DS_TOOL_ERROR_READING_SCHEMA.get( 1076 fileList, StaticUtils.getExceptionMessage(e)), 1077 e); 1078 } 1079 } 1080 else 1081 { 1082 serverConfig.setSchema(null); 1083 } 1084 1085 1086 // If an additional bind DN and password are provided, then include them in 1087 // the configuration. 1088 if (additionalBindDNArgument.isPresent()) 1089 { 1090 serverConfig.addAdditionalBindCredentials( 1091 additionalBindDNArgument.getValue().toString(), 1092 additionalBindPasswordArgument.getValue()); 1093 } 1094 1095 1096 // If a maximum number of changelog entries was specified, then update the 1097 // configuration with that. 1098 if (maxChangeLogEntriesArgument.isPresent()) 1099 { 1100 serverConfig.setMaxChangeLogEntries( 1101 maxChangeLogEntriesArgument.getValue()); 1102 } 1103 1104 1105 // If a maximum number of concurrent connections was specified, then update 1106 // the configuration with that. 1107 if (maxConcurrentConnectionsArgument.isPresent()) 1108 { 1109 serverConfig.setMaxConnections( 1110 maxConcurrentConnectionsArgument.getValue()); 1111 } 1112 1113 1114 // If a size limit was specified, then update the configuration with that. 1115 if (sizeLimitArgument.isPresent()) 1116 { 1117 serverConfig.setMaxSizeLimit(sizeLimitArgument.getValue()); 1118 } 1119 1120 1121 // If the password argument was specified, then set the password arguments. 1122 if (passwordAttributeArgument.isPresent()) 1123 { 1124 serverConfig.setPasswordAttributes(passwordAttributeArgument.getValues()); 1125 } 1126 1127 1128 // Configure password encodings for the server. 1129 final LinkedHashMap<String,InMemoryPasswordEncoder> passwordEncoders = 1130 new LinkedHashMap<>(10); 1131 addUnsaltedEncoder("MD5", "MD5", passwordEncoders); 1132 addUnsaltedEncoder("SHA", "SHA-1", passwordEncoders); 1133 addUnsaltedEncoder("SHA1", "SHA-1", passwordEncoders); 1134 addUnsaltedEncoder("SHA-1", "SHA-1", passwordEncoders); 1135 addUnsaltedEncoder("SHA256", "SHA-256", passwordEncoders); 1136 addUnsaltedEncoder("SHA-256", "SHA-256", passwordEncoders); 1137 addUnsaltedEncoder("SHA384", "SHA-384", passwordEncoders); 1138 addUnsaltedEncoder("SHA-384", "SHA-384", passwordEncoders); 1139 addUnsaltedEncoder("SHA512", "SHA-512", passwordEncoders); 1140 addUnsaltedEncoder("SHA-512", "SHA-512", passwordEncoders); 1141 addSaltedEncoder("SMD5", "MD5", passwordEncoders); 1142 addSaltedEncoder("SSHA", "SHA-1", passwordEncoders); 1143 addSaltedEncoder("SSHA1", "SHA-1", passwordEncoders); 1144 addSaltedEncoder("SSHA-1", "SHA-1", passwordEncoders); 1145 addSaltedEncoder("SSHA256", "SHA-256", passwordEncoders); 1146 addSaltedEncoder("SSHA-256", "SHA-256", passwordEncoders); 1147 addSaltedEncoder("SSHA384", "SHA-384", passwordEncoders); 1148 addSaltedEncoder("SSHA-384", "SHA-384", passwordEncoders); 1149 addSaltedEncoder("SSHA512", "SHA-512", passwordEncoders); 1150 addSaltedEncoder("SSHA-512", "SHA-512", passwordEncoders); 1151 addClearEncoder("CLEAR", null, passwordEncoders); 1152 addClearEncoder("BASE64", 1153 Base64PasswordEncoderOutputFormatter.getInstance(), passwordEncoders); 1154 addClearEncoder("HEX", 1155 HexPasswordEncoderOutputFormatter.getLowercaseInstance(), 1156 passwordEncoders); 1157 1158 final InMemoryPasswordEncoder primaryEncoder; 1159 if (defaultPasswordEncodingArgument.isPresent()) 1160 { 1161 primaryEncoder = passwordEncoders.remove( 1162 StaticUtils.toLowerCase(defaultPasswordEncodingArgument.getValue())); 1163 if (primaryEncoder == null) 1164 { 1165 throw new LDAPException(ResultCode.PARAM_ERROR, 1166 ERR_MEM_DS_TOOL_UNAVAILABLE_PW_ENCODING.get( 1167 defaultPasswordEncodingArgument.getValue(), 1168 String.valueOf(passwordEncoders.keySet()))); 1169 } 1170 } 1171 else 1172 { 1173 primaryEncoder = null; 1174 } 1175 1176 serverConfig.setPasswordEncoders(primaryEncoder, 1177 passwordEncoders.values()); 1178 1179 1180 // Configure the allowed operation types. 1181 if (allowedOperationTypeArgument.isPresent()) 1182 { 1183 final EnumSet<OperationType> operationTypes = 1184 EnumSet.noneOf(OperationType.class); 1185 for (final String operationTypeName : 1186 allowedOperationTypeArgument.getValues()) 1187 { 1188 final OperationType name = OperationType.forName(operationTypeName); 1189 if (name == null) 1190 { 1191 throw new LDAPException(ResultCode.PARAM_ERROR, 1192 ERR_MEM_DS_TOOL_UNSUPPORTED_ALLOWED_OP_TYPE.get(name)); 1193 } 1194 else 1195 { 1196 switch (name) 1197 { 1198 case ADD: 1199 case BIND: 1200 case COMPARE: 1201 case DELETE: 1202 case EXTENDED: 1203 case MODIFY: 1204 case MODIFY_DN: 1205 case SEARCH: 1206 operationTypes.add(name); 1207 break; 1208 case ABANDON: 1209 case UNBIND: 1210 default: 1211 throw new LDAPException(ResultCode.PARAM_ERROR, 1212 ERR_MEM_DS_TOOL_UNSUPPORTED_ALLOWED_OP_TYPE.get(name)); 1213 } 1214 } 1215 } 1216 1217 serverConfig.setAllowedOperationTypes(operationTypes); 1218 } 1219 1220 1221 // Configure the authentication required operation types. 1222 if (authenticationRequiredOperationTypeArgument.isPresent()) 1223 { 1224 final EnumSet<OperationType> operationTypes = 1225 EnumSet.noneOf(OperationType.class); 1226 for (final String operationTypeName : 1227 authenticationRequiredOperationTypeArgument.getValues()) 1228 { 1229 final OperationType name = OperationType.forName(operationTypeName); 1230 if (name == null) 1231 { 1232 throw new LDAPException(ResultCode.PARAM_ERROR, 1233 ERR_MEM_DS_TOOL_UNSUPPORTED_AUTH_REQUIRED_OP_TYPE.get(name)); 1234 } 1235 else 1236 { 1237 switch (name) 1238 { 1239 case ADD: 1240 case COMPARE: 1241 case DELETE: 1242 case EXTENDED: 1243 case MODIFY: 1244 case MODIFY_DN: 1245 case SEARCH: 1246 operationTypes.add(name); 1247 break; 1248 case ABANDON: 1249 case UNBIND: 1250 default: 1251 throw new LDAPException(ResultCode.PARAM_ERROR, 1252 ERR_MEM_DS_TOOL_UNSUPPORTED_AUTH_REQUIRED_OP_TYPE.get(name)); 1253 } 1254 } 1255 } 1256 1257 serverConfig.setAuthenticationRequiredOperationTypes(operationTypes); 1258 } 1259 1260 1261 // If an access log file was specified, then create the appropriate log 1262 // handler. 1263 if (accessLogToStandardOutArgument.isPresent()) 1264 { 1265 final StreamHandler handler = new StreamHandler(System.out, 1266 new MinimalLogFormatter(null, false, false, true)); 1267 handler.setLevel(Level.INFO); 1268 serverConfig.setAccessLogHandler(handler); 1269 } 1270 else if (accessLogFileArgument.isPresent()) 1271 { 1272 final File logFile = accessLogFileArgument.getValue(); 1273 try 1274 { 1275 final FileHandler handler = 1276 new FileHandler(logFile.getAbsolutePath(), true); 1277 handler.setLevel(Level.INFO); 1278 handler.setFormatter(new MinimalLogFormatter(null, false, false, 1279 true)); 1280 serverConfig.setAccessLogHandler(handler); 1281 } 1282 catch (final Exception e) 1283 { 1284 Debug.debugException(e); 1285 throw new LDAPException(ResultCode.LOCAL_ERROR, 1286 ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get( 1287 logFile.getAbsolutePath(), 1288 StaticUtils.getExceptionMessage(e)), 1289 e); 1290 } 1291 } 1292 1293 1294 // If an LDAP debug log file was specified, then create the appropriate log 1295 // handler. 1296 if (ldapDebugLogToStandardOutArgument.isPresent()) 1297 { 1298 final StreamHandler handler = new StreamHandler(System.out, 1299 new MinimalLogFormatter(null, false, false, true)); 1300 handler.setLevel(Level.INFO); 1301 serverConfig.setLDAPDebugLogHandler(handler); 1302 } 1303 else if (ldapDebugLogFileArgument.isPresent()) 1304 { 1305 final File logFile = ldapDebugLogFileArgument.getValue(); 1306 try 1307 { 1308 final FileHandler handler = 1309 new FileHandler(logFile.getAbsolutePath(), true); 1310 handler.setLevel(Level.INFO); 1311 handler.setFormatter(new MinimalLogFormatter(null, false, false, 1312 true)); 1313 serverConfig.setLDAPDebugLogHandler(handler); 1314 } 1315 catch (final Exception e) 1316 { 1317 Debug.debugException(e); 1318 throw new LDAPException(ResultCode.LOCAL_ERROR, 1319 ERR_MEM_DS_TOOL_ERROR_CREATING_LOG_HANDLER.get( 1320 logFile.getAbsolutePath(), 1321 StaticUtils.getExceptionMessage(e)), 1322 e); 1323 } 1324 } 1325 1326 1327 // If a code log file was specified, then update the configuration 1328 // accordingly. 1329 if (codeLogFile.isPresent()) 1330 { 1331 serverConfig.setCodeLogDetails(codeLogFile.getValue().getAbsolutePath(), 1332 true); 1333 } 1334 1335 1336 // If SSL is to be used, then create the corresponding socket factories. 1337 if (useSSLArgument.isPresent() || useStartTLSArgument.isPresent()) 1338 { 1339 final File keyStorePath; 1340 final char[] keyStorePIN; 1341 final String keyStoreType; 1342 if (keyStorePathArgument.isPresent()) 1343 { 1344 keyStorePath = keyStorePathArgument.getValue(); 1345 keyStorePIN = keyStorePasswordArgument.getValue().toCharArray(); 1346 keyStoreType = keyStoreTypeArgument.getValue(); 1347 } 1348 else 1349 { 1350 try 1351 { 1352 keyStoreType = "JKS"; 1353 final ObjectPair<File,char[]> keyStoreInfo = 1354 SelfSignedCertificateGenerator. 1355 generateTemporarySelfSignedCertificate( 1356 getToolName(), keyStoreType); 1357 keyStorePath = keyStoreInfo.getFirst(); 1358 keyStorePIN = keyStoreInfo.getSecond(); 1359 } 1360 catch (final CertException e) 1361 { 1362 Debug.debugException(e); 1363 throw new LDAPException(ResultCode.LOCAL_ERROR, e.getMessage(), e); 1364 } 1365 } 1366 1367 1368 try 1369 { 1370 final KeyManager keyManager = new KeyStoreKeyManager(keyStorePath, 1371 keyStorePIN, keyStoreType, null); 1372 1373 final TrustManager trustManager; 1374 if (trustStorePathArgument.isPresent()) 1375 { 1376 final char[] password; 1377 if (trustStorePasswordArgument.isPresent()) 1378 { 1379 password = trustStorePasswordArgument.getValue().toCharArray(); 1380 } 1381 else 1382 { 1383 password = null; 1384 } 1385 1386 trustManager = new TrustStoreTrustManager( 1387 trustStorePathArgument.getValue(), password, 1388 trustStoreTypeArgument.getValue(), true); 1389 } 1390 else 1391 { 1392 trustManager = new TrustAllTrustManager(); 1393 } 1394 1395 final SSLUtil serverSSLUtil = new SSLUtil(keyManager, trustManager); 1396 1397 if (useSSLArgument.isPresent()) 1398 { 1399 final SSLUtil clientSSLUtil = new SSLUtil(new TrustAllTrustManager()); 1400 serverConfig.setListenerConfigs( 1401 InMemoryListenerConfig.createLDAPSConfig("LDAPS", null, 1402 listenPort, serverSSLUtil.createSSLServerSocketFactory(), 1403 clientSSLUtil.createSSLSocketFactory())); 1404 } 1405 else 1406 { 1407 serverConfig.setListenerConfigs( 1408 InMemoryListenerConfig.createLDAPConfig("LDAP+StartTLS", null, 1409 listenPort, serverSSLUtil.createSSLSocketFactory())); 1410 } 1411 } 1412 catch (final Exception e) 1413 { 1414 Debug.debugException(e); 1415 throw new LDAPException(ResultCode.LOCAL_ERROR, 1416 ERR_MEM_DS_TOOL_ERROR_INITIALIZING_SSL.get( 1417 StaticUtils.getExceptionMessage(e)), 1418 e); 1419 } 1420 } 1421 else 1422 { 1423 serverConfig.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig( 1424 "LDAP", listenPort)); 1425 } 1426 1427 1428 // If vendor name and/or vendor version values were provided, then configure 1429 // them for use. 1430 if (vendorNameArgument.isPresent()) 1431 { 1432 serverConfig.setVendorName(vendorNameArgument.getValue()); 1433 } 1434 1435 if (vendorVersionArgument.isPresent()) 1436 { 1437 serverConfig.setVendorVersion(vendorVersionArgument.getValue()); 1438 } 1439 1440 1441 // If equality indexing is to be performed, then configure it. 1442 if (equalityIndexArgument.isPresent()) 1443 { 1444 serverConfig.setEqualityIndexAttributes( 1445 equalityIndexArgument.getValues()); 1446 } 1447 1448 return serverConfig; 1449 } 1450 1451 1452 1453 /** 1454 * Updates the map with an unsalted password encoder with the provided 1455 * information. 1456 * 1457 * @param schemeName The name to use to identify the scheme, without 1458 * the curly braces. 1459 * @param digestAlgorithm The name of the message digest algorithm to use 1460 * for the password encoder. 1461 * @param encoderMap The map to which the encoder will bea added. 1462 */ 1463 private static void addUnsaltedEncoder(final String schemeName, 1464 final String digestAlgorithm, 1465 final Map<String,InMemoryPasswordEncoder> encoderMap) 1466 { 1467 try 1468 { 1469 final UnsaltedMessageDigestInMemoryPasswordEncoder encoder = 1470 new UnsaltedMessageDigestInMemoryPasswordEncoder( 1471 '{' + schemeName + '}', 1472 Base64PasswordEncoderOutputFormatter.getInstance(), 1473 MessageDigest.getInstance(digestAlgorithm)); 1474 encoderMap.put(StaticUtils.toLowerCase(schemeName), encoder); 1475 } 1476 catch (final Exception e) 1477 { 1478 Debug.debugException(e); 1479 } 1480 } 1481 1482 1483 1484 /** 1485 * Updates the map with a salted password encoder with the provided 1486 * information. 1487 * 1488 * @param schemeName The name to use to identify the scheme, without 1489 * the curly braces. 1490 * @param digestAlgorithm The name of the message digest algorithm to use 1491 * for the password encoder. 1492 * @param encoderMap The map to which the encoder will bea added. 1493 */ 1494 private static void addSaltedEncoder(final String schemeName, 1495 final String digestAlgorithm, 1496 final Map<String,InMemoryPasswordEncoder> encoderMap) 1497 { 1498 try 1499 { 1500 final SaltedMessageDigestInMemoryPasswordEncoder encoder = 1501 new SaltedMessageDigestInMemoryPasswordEncoder( 1502 '{' + schemeName + '}', 1503 Base64PasswordEncoderOutputFormatter.getInstance(), 1504 MessageDigest.getInstance(digestAlgorithm), 8, true, true); 1505 encoderMap.put(StaticUtils.toLowerCase(schemeName), encoder); 1506 } 1507 catch (final Exception e) 1508 { 1509 Debug.debugException(e); 1510 } 1511 } 1512 1513 1514 1515 /** 1516 * Updates the map with a clear-text password encoder with the provided 1517 * information. 1518 * 1519 * @param schemeName The name to use to identify the scheme, without 1520 * the curly braces. 1521 * @param outputFormatter The output formatter to use. It may be 1522 * {@code null} if the output should remain in the 1523 * clear. 1524 * @param encoderMap The map to which the encoder will bea added. 1525 */ 1526 private static void addClearEncoder(final String schemeName, 1527 final PasswordEncoderOutputFormatter outputFormatter, 1528 final Map<String,InMemoryPasswordEncoder> encoderMap) 1529 { 1530 final ClearInMemoryPasswordEncoder encoder = 1531 new ClearInMemoryPasswordEncoder('{' + schemeName + '}', 1532 outputFormatter); 1533 encoderMap.put(StaticUtils.toLowerCase(schemeName), encoder); 1534 } 1535 1536 1537 1538 /** 1539 * {@inheritDoc} 1540 */ 1541 @Override() 1542 public LinkedHashMap<String[],String> getExampleUsages() 1543 { 1544 final LinkedHashMap<String[],String> exampleUsages = 1545 new LinkedHashMap<>(StaticUtils.computeMapCapacity(2)); 1546 1547 final String[] example1Args = 1548 { 1549 "--baseDN", "dc=example,dc=com" 1550 }; 1551 exampleUsages.put(example1Args, INFO_MEM_DS_TOOL_EXAMPLE_1.get()); 1552 1553 final String[] example2Args = 1554 { 1555 "--baseDN", "dc=example,dc=com", 1556 "--port", "1389", 1557 "--ldifFile", "test.ldif", 1558 "--accessLogFile", "access.log", 1559 "--useDefaultSchema" 1560 }; 1561 exampleUsages.put(example2Args, INFO_MEM_DS_TOOL_EXAMPLE_2.get()); 1562 1563 return exampleUsages; 1564 } 1565 1566 1567 1568 /** 1569 * Retrieves the in-memory directory server instance that has been created by 1570 * this tool. It will only be valid after the {@link #doToolProcessing()} 1571 * method has been called. 1572 * 1573 * @return The in-memory directory server instance that has been created by 1574 * this tool, or {@code null} if the directory server instance has 1575 * not been successfully created. 1576 */ 1577 public InMemoryDirectoryServer getDirectoryServer() 1578 { 1579 return directoryServer; 1580 } 1581 1582 1583 1584 /** 1585 * {@inheritDoc} 1586 */ 1587 @Override() 1588 public void connectionCreationFailure(final Socket socket, 1589 final Throwable cause) 1590 { 1591 err(ERR_MEM_DS_TOOL_ERROR_ACCEPTING_CONNECTION.get( 1592 StaticUtils.getExceptionMessage(cause))); 1593 } 1594 1595 1596 1597 /** 1598 * {@inheritDoc} 1599 */ 1600 @Override() 1601 public void connectionTerminated( 1602 final LDAPListenerClientConnection connection, 1603 final LDAPException cause) 1604 { 1605 err(ERR_MEM_DS_TOOL_CONNECTION_TERMINATED_BY_EXCEPTION.get( 1606 StaticUtils.getExceptionMessage(cause))); 1607 } 1608}