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 2008 Sun Microsystems, Inc.
026 */
027
028 package org.opends.server.util.args;
029
030 import org.opends.messages.Message;
031 import static org.opends.messages.ToolMessages.*;
032 import org.opends.server.tools.LDAPConnection;
033 import org.opends.server.tools.LDAPConnectionOptions;
034 import org.opends.server.tools.SSLConnectionFactory;
035 import org.opends.server.tools.SSLConnectionException;
036 import org.opends.server.tools.LDAPConnectionException;
037 import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
038 import static org.opends.server.util.StaticUtils.wrapText;
039 import org.opends.server.util.cli.LDAPConnectionConsoleInteraction;
040 import org.opends.server.admin.client.cli.SecureConnectionCliArgs;
041 import org.opends.server.types.OpenDsException;
042
043 import java.util.LinkedList;
044 import java.util.LinkedHashSet;
045 import java.util.concurrent.atomic.AtomicInteger;
046 import java.io.PrintStream;
047
048 /**
049 * Creates an argument parser pre-populated with arguments for specifying
050 * information for openning and LDAPConnection an LDAP connection.
051 */
052 public class LDAPConnectionArgumentParser extends ArgumentParser {
053
054 private SecureConnectionCliArgs args;
055
056 /**
057 * Creates a new instance of this argument parser with no arguments.
058 * Unnamed trailing arguments will not be allowed.
059 *
060 * @param mainClassName The fully-qualified name of the Java
061 * class that should be invoked to launch
062 * the program with which this argument
063 * parser is associated.
064 * @param toolDescription A human-readable description for the
065 * tool, which will be included when
066 * displaying usage information.
067 * @param longArgumentsCaseSensitive Indicates whether long arguments should
068 * @param argumentGroup Group to which LDAP arguments will be
069 * added to the parser. May be null to
070 * indicate that arguments should be
071 * added to the default group
072 */
073 public LDAPConnectionArgumentParser(String mainClassName,
074 Message toolDescription,
075 boolean longArgumentsCaseSensitive,
076 ArgumentGroup argumentGroup) {
077 super(mainClassName, toolDescription, longArgumentsCaseSensitive);
078 addLdapConnectionArguments(argumentGroup);
079 }
080
081 /**
082 * Creates a new instance of this argument parser with no arguments that may
083 * or may not be allowed to have unnamed trailing arguments.
084 *
085 * @param mainClassName The fully-qualified name of the Java
086 * class that should be invoked to launch
087 * the program with which this argument
088 * parser is associated.
089 * @param toolDescription A human-readable description for the
090 * tool, which will be included when
091 * displaying usage information.
092 * @param longArgumentsCaseSensitive Indicates whether long arguments should
093 * be treated in a case-sensitive manner.
094 * @param allowsTrailingArguments Indicates whether this parser allows
095 * unnamed trailing arguments to be
096 * provided.
097 * @param minTrailingArguments The minimum number of unnamed trailing
098 * arguments that must be provided. A
099 * value less than or equal to zero
100 * indicates that no minimum will be
101 * enforced.
102 * @param maxTrailingArguments The maximum number of unnamed trailing
103 * arguments that may be provided. A
104 * value less than or equal to zero
105 * indicates that no maximum will be
106 * enforced.
107 * @param trailingArgsDisplayName The display name that should be used
108 * as a placeholder for unnamed trailing
109 * arguments in the generated usage
110 * information.
111 * @param argumentGroup Group to which LDAP arguments will be
112 * added to the parser. May be null to
113 * indicate that arguments should be
114 * added to the default group
115 */
116 public LDAPConnectionArgumentParser(String mainClassName,
117 Message toolDescription,
118 boolean longArgumentsCaseSensitive,
119 boolean allowsTrailingArguments,
120 int minTrailingArguments,
121 int maxTrailingArguments,
122 String trailingArgsDisplayName,
123 ArgumentGroup argumentGroup) {
124 super(mainClassName, toolDescription, longArgumentsCaseSensitive,
125 allowsTrailingArguments, minTrailingArguments, maxTrailingArguments,
126 trailingArgsDisplayName);
127 addLdapConnectionArguments(argumentGroup);
128 }
129
130 /**
131 * Indicates whether or not the user has indicated that they would like
132 * to perform a remote operation based on the arguments.
133 *
134 * @return true if the user wants to perform a remote operation;
135 * false otherwise
136 */
137 public boolean connectionArgumentsPresent() {
138 return args != null && args.argumentsPresent();
139 }
140
141 /**
142 * Creates a new LDAPConnection and invokes a connect operation using
143 * information provided in the parsed set of arguments that were provided
144 * by the user.
145 *
146 * @param out stream to write messages
147 * @param err stream to write messages
148 * @return LDAPConnection created by this class from parsed arguments
149 * @throws LDAPConnectionException if there was a problem connecting
150 * to the server indicated by the input arguments
151 * @throws ArgumentException if there was a problem processing the input
152 * arguments
153 */
154 public LDAPConnection connect(PrintStream out, PrintStream err)
155 throws LDAPConnectionException, ArgumentException
156 {
157 return connect(this.args, out, err);
158 }
159
160
161 /**
162 * Creates a new LDAPConnection and invokes a connect operation using
163 * information provided in the parsed set of arguments that were provided
164 * by the user.
165 *
166 * @param args with which to connect
167 * @param out stream to write messages
168 * @param err stream to write messages
169 * @return LDAPConnection created by this class from parsed arguments
170 * @throws LDAPConnectionException if there was a problem connecting
171 * to the server indicated by the input arguments
172 * @throws ArgumentException if there was a problem processing the input
173 * arguments
174 */
175 private LDAPConnection connect(SecureConnectionCliArgs args,
176 PrintStream out, PrintStream err)
177 throws LDAPConnectionException, ArgumentException
178 {
179 // If both a bind password and bind password file were provided, then return
180 // an error.
181 if (args.bindPasswordArg.isPresent() &&
182 args.bindPasswordFileArg.isPresent())
183 {
184 Message message = ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
185 args.bindPasswordArg.getLongIdentifier(),
186 args.bindPasswordFileArg.getLongIdentifier());
187 err.println(wrapText(message, MAX_LINE_WIDTH));
188 throw new ArgumentException(message);
189 }
190
191
192 // If both a key store password and key store password file were provided,
193 // then return an error.
194 if (args.keyStorePasswordArg.isPresent() &&
195 args.keyStorePasswordFileArg.isPresent())
196 {
197 Message message = ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
198 args.keyStorePasswordArg.getLongIdentifier(),
199 args.keyStorePasswordFileArg.getLongIdentifier());
200 throw new ArgumentException(message);
201 }
202
203
204 // If both a trust store password and trust store password file were
205 // provided, then return an error.
206 if (args.trustStorePasswordArg.isPresent() &&
207 args.trustStorePasswordFileArg.isPresent())
208 {
209 Message message = ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
210 args.trustStorePasswordArg.getLongIdentifier(),
211 args.trustStorePasswordFileArg.getLongIdentifier());
212 err.println(wrapText(message, MAX_LINE_WIDTH));
213 throw new ArgumentException(message);
214 }
215
216 // Create the LDAP connection options object, which will be used to
217 // customize the way that we connect to the server and specify a set of
218 // basic defaults.
219 LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
220 connectionOptions.setVersionNumber(3);
221
222
223 // See if we should use SSL or StartTLS when establishing the connection.
224 // If so, then make sure only one of them was specified.
225 if (args.useSSLArg.isPresent())
226 {
227 if (args.useStartTLSArg.isPresent())
228 {
229 Message message = ERR_LDAP_CONN_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
230 args.useSSLArg.getLongIdentifier(),
231 args.useSSLArg.getLongIdentifier());
232 err.println(wrapText(message, MAX_LINE_WIDTH));
233 throw new ArgumentException(message);
234 }
235 else
236 {
237 connectionOptions.setUseSSL(true);
238 }
239 }
240 else if (args.useStartTLSArg.isPresent())
241 {
242 connectionOptions.setStartTLS(true);
243 }
244
245
246 // If we should blindly trust any certificate, then install the appropriate
247 // SSL connection factory.
248 if (args.useSSLArg.isPresent() || args.useStartTLSArg.isPresent())
249 {
250 try
251 {
252 String clientAlias;
253 if (args.certNicknameArg.isPresent())
254 {
255 clientAlias = args.certNicknameArg.getValue();
256 }
257 else
258 {
259 clientAlias = null;
260 }
261
262 SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
263 sslConnectionFactory.init(args.trustAllArg.isPresent(),
264 args.keyStorePathArg.getValue(),
265 args.keyStorePasswordArg.getValue(),
266 clientAlias,
267 args.trustStorePathArg.getValue(),
268 args.trustStorePasswordArg.getValue());
269
270 connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
271 }
272 catch (SSLConnectionException sce)
273 {
274 Message message =
275 ERR_LDAP_CONN_CANNOT_INITIALIZE_SSL.get(sce.getMessage());
276 err.println(wrapText(message, MAX_LINE_WIDTH));
277 }
278 }
279
280
281 // If one or more SASL options were provided, then make sure that one of
282 // them was "mech" and specified a valid SASL mechanism.
283 if (args.saslOptionArg.isPresent())
284 {
285 String mechanism = null;
286 LinkedList<String> options = new LinkedList<String>();
287
288 for (String s : args.saslOptionArg.getValues())
289 {
290 int equalPos = s.indexOf('=');
291 if (equalPos <= 0)
292 {
293 Message message = ERR_LDAP_CONN_CANNOT_PARSE_SASL_OPTION.get(s);
294 err.println(wrapText(message, MAX_LINE_WIDTH));
295 throw new ArgumentException(message);
296 }
297 else
298 {
299 String name = s.substring(0, equalPos);
300
301 if (name.equalsIgnoreCase("mech"))
302 {
303 mechanism = s;
304 }
305 else
306 {
307 options.add(s);
308 }
309 }
310 }
311
312 if (mechanism == null)
313 {
314 Message message = ERR_LDAP_CONN_NO_SASL_MECHANISM.get();
315 err.println(wrapText(message, MAX_LINE_WIDTH));
316 throw new ArgumentException(message);
317 }
318
319 connectionOptions.setSASLMechanism(mechanism);
320
321 for (String option : options)
322 {
323 connectionOptions.addSASLProperty(option);
324 }
325 }
326 return connect(
327 args.hostNameArg.getValue(),
328 args.portArg.getIntValue(),
329 args.bindDnArg.getValue(),
330 getPasswordValue(args.bindPasswordArg, args.bindPasswordFileArg),
331 connectionOptions, out, err);
332 }
333
334 /**
335 * Creates a connection using a console interaction that will be used
336 * to potientially interact with the user to prompt for necessary
337 * information for establishing the connection.
338 *
339 * @param ui user interaction for prompting the user
340 * @param out stream to write messages
341 * @param err stream to write messages
342 * @return LDAPConnection created by this class from parsed arguments
343 * @throws LDAPConnectionException if there was a problem connecting
344 * to the server indicated by the input arguments
345 */
346 public LDAPConnection connect(LDAPConnectionConsoleInteraction ui,
347 PrintStream out, PrintStream err)
348 throws LDAPConnectionException
349 {
350 LDAPConnection connection = null;
351 try {
352 ui.run();
353 LDAPConnectionOptions options = new LDAPConnectionOptions();
354 options.setVersionNumber(3);
355 connection = connect(
356 ui.getHostName(),
357 ui.getPortNumber(),
358 ui.getBindDN(),
359 ui.getBindPassword(),
360 ui.populateLDAPOptions(options), out, err);
361 } catch (OpenDsException e) {
362 err.println(e.getMessageObject());
363 }
364 return connection;
365 }
366
367
368 /**
369 * Creates a connection from information provided.
370 *
371 * @param host of the server
372 * @param port of the server
373 * @param bindDN with which to connect
374 * @param bindPw with which to connect
375 * @param options with which to connect
376 * @param out stream to write messages
377 * @param err stream to write messages
378 * @return LDAPConnection created by this class from parsed arguments
379 * @throws LDAPConnectionException if there was a problem connecting
380 * to the server indicated by the input arguments
381 */
382 public LDAPConnection connect(String host, int port,
383 String bindDN, String bindPw,
384 LDAPConnectionOptions options,
385 PrintStream out,
386 PrintStream err)
387 throws LDAPConnectionException
388 {
389
390 // Attempt to connect and authenticate to the Directory Server.
391 AtomicInteger nextMessageID = new AtomicInteger(1);
392
393 LDAPConnection connection = new LDAPConnection(
394 host, port, options, out, err);
395
396 connection.connectToHost(bindDN, bindPw, nextMessageID);
397
398 return connection;
399 }
400
401 /**
402 * Gets the arguments associated with this parser.
403 *
404 * @return arguments for this parser.
405 */
406 public SecureConnectionCliArgs getArguments() {
407 return args;
408 }
409
410 /**
411 * Commodity method that retrieves the password value analyzing the contents
412 * of a string argument and of a file based argument. It assumes that the
413 * arguments have already been parsed and validated.
414 * @param bindPwdArg the string argument.
415 * @param bindPwdFileArg the file based argument.
416 * @return the password value.
417 */
418 public static String getPasswordValue(StringArgument bindPwdArg,
419 FileBasedArgument bindPwdFileArg)
420 {
421 String pwd = bindPwdArg.getValue();
422 if ((pwd == null) && bindPwdFileArg.isPresent())
423 {
424 pwd = bindPwdFileArg.getValue();
425 }
426 return pwd;
427 }
428
429 private void addLdapConnectionArguments(ArgumentGroup argGroup) {
430 args = new SecureConnectionCliArgs();
431 try {
432 LinkedHashSet<Argument> argSet = args.createGlobalArguments();
433 for (Argument arg : argSet) {
434 addArgument(arg, argGroup);
435 }
436 }
437 catch (ArgumentException ae) {
438 ae.printStackTrace(); // Should never happen
439 }
440
441 }
442
443 }