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.server.admin.client.cli;
029    
030    import static org.opends.server.admin.client.cli.DsFrameworkCliReturnCode.*;
031    import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
032    import static org.opends.server.loggers.debug.DebugLogger.getTracer;
033    import static org.opends.messages.ToolMessages.*;
034    
035    import org.opends.messages.Message;
036    import org.opends.messages.MessageBuilder;
037    import static org.opends.server.tools.ToolConstants.*;
038    import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
039    import static org.opends.server.util.StaticUtils.wrapText;
040    
041    import java.io.IOException;
042    import java.io.OutputStream;
043    import java.io.PrintStream;
044    import java.util.Collection;
045    import java.util.LinkedHashSet;
046    import java.util.logging.Logger;
047    
048    import javax.net.ssl.KeyManager;
049    
050    import org.opends.admin.ads.util.ApplicationTrustManager;
051    import org.opends.server.loggers.debug.DebugTracer;
052    import org.opends.server.types.DebugLogLevel;
053    import org.opends.server.util.PasswordReader;
054    import org.opends.server.util.args.Argument;
055    import org.opends.server.util.args.ArgumentException;
056    import org.opends.server.util.args.BooleanArgument;
057    import org.opends.server.util.args.FileBasedArgument;
058    import org.opends.server.util.args.StringArgument;
059    import org.opends.server.util.args.SubCommandArgumentParser;
060    import org.opends.server.util.args.ArgumentGroup;
061    
062    /**
063     * This is a commodity class that can be used to check the arguments required
064     * to establish a secure connection in the command line.  It can be used
065     * to generate an ApplicationTrustManager object based on the options provided
066     * by the user in the command line.
067     *
068     */
069    public abstract class SecureConnectionCliParser extends SubCommandArgumentParser
070    {
071      /**
072       * The showUsage' global argument.
073       */
074      protected BooleanArgument showUsageArg = null;
075    
076      /**
077       * The 'verbose' global argument.
078       */
079      protected BooleanArgument verboseArg = null;
080    
081      /**
082       * The secure args list object.
083       */
084      protected SecureConnectionCliArgs secureArgsList ;
085    
086      /**
087       * Argument indicating a properties file argument.
088       */
089      protected StringArgument  propertiesFileArg = null;
090    
091      /**
092       * The argument which should be used to indicate that we will not
093       * look for properties file.
094       */
095      protected BooleanArgument noPropertiesFileArg;
096    
097      /**
098       * The tracer object for the debug logger.
099       */
100      private static final DebugTracer TRACER = getTracer();
101    
102      /**
103       * End Of Line.
104       */
105      public static String EOL = System.getProperty("line.separator");
106    
107      /**
108       * The Logger.
109       */
110      static private final Logger LOG =
111        Logger.getLogger(SecureConnectionCliParser.class.getName());
112    
113      /**
114       * Creates a new instance of this argument parser with no arguments.
115       *
116       * @param mainClassName
117       *          The fully-qualified name of the Java class that should
118       *          be invoked to launch the program with which this
119       *          argument parser is associated.
120       * @param toolDescription
121       *          A human-readable description for the tool, which will be
122       *          included when displaying usage information.
123       * @param longArgumentsCaseSensitive
124       *          Indicates whether subcommand and long argument names
125       *          should be treated in a case-sensitive manner.
126       */
127      protected SecureConnectionCliParser(String mainClassName,
128          Message toolDescription, boolean longArgumentsCaseSensitive)
129      {
130        super(mainClassName, toolDescription, longArgumentsCaseSensitive);
131      }
132    
133      /**
134       * Get the bindDN which has to be used for the command.
135       *
136       * @return The bindDN specified by the command line argument, or the
137       *         default value, if not specified.
138       */
139      public String getBindDN()
140      {
141        return secureArgsList.getBindDN();
142      }
143    
144    
145      /**
146       * Returns the Administrator UID provided in the command-line.
147       * @return the Administrator UID provided in the command-line.
148       */
149      public String getAdministratorUID()
150      {
151        return secureArgsList.getAdministratorUID();
152      }
153    
154      /**
155       * Get the password which has to be used for the command.
156       *
157       * @param dn
158       *          The user DN for which to password could be asked.
159       * @param out
160       *          The input stream to used if we have to prompt to the
161       *          user.
162       * @param err
163       *          The error stream to used if we have to prompt to the
164       *          user.
165       * @param clearArg
166       *          The password StringArgument argument.
167       * @param fileArg
168       *          The password FileBased argument.
169       * @return The password stored into the specified file on by the
170       *         command line argument, or prompts it if not specified.
171       */
172      protected String getBindPassword(String dn,
173          OutputStream out, OutputStream err, StringArgument clearArg,
174          FileBasedArgument fileArg)
175      {
176        if (clearArg.isPresent())
177        {
178          String bindPasswordValue = clearArg.getValue();
179          if(bindPasswordValue != null && bindPasswordValue.equals("-"))
180          {
181            // read the password from the stdin.
182            try
183            {
184              out.write(INFO_LDAPAUTH_PASSWORD_PROMPT.get(dn).getBytes());
185              out.flush();
186              char[] pwChars = PasswordReader.readPassword();
187              bindPasswordValue = new String(pwChars);
188            } catch(Exception ex)
189            {
190              if (debugEnabled())
191              {
192                TRACER.debugCaught(DebugLogLevel.ERROR, ex);
193              }
194              try
195              {
196                err.write(wrapText(ex.getMessage(), MAX_LINE_WIDTH).getBytes());
197                err.write(EOL.getBytes());
198              }
199              catch (IOException e)
200              {
201              }
202              return null;
203            }
204          }
205          return bindPasswordValue;
206        }
207        else
208        if (fileArg.isPresent())
209        {
210          return fileArg.getValue();
211        }
212        else
213        {
214          // read the password from the stdin.
215          try
216          {
217            out.write(INFO_LDAPAUTH_PASSWORD_PROMPT.get(dn).toString().getBytes());
218            out.flush();
219            char[] pwChars = PasswordReader.readPassword();
220            return new String(pwChars);
221          }
222          catch (Exception ex)
223          {
224            if (debugEnabled())
225            {
226              TRACER.debugCaught(DebugLogLevel.ERROR, ex);
227            }
228            try
229            {
230              err.write(wrapText(ex.getMessage(), MAX_LINE_WIDTH).getBytes());
231              err.write(EOL.getBytes());
232            }
233            catch (IOException e)
234            {
235            }
236            return null;
237          }
238        }
239    
240      }
241    
242      /**
243       * Get the password which has to be used for the command.
244       *
245       * @param dn
246       *          The user DN for which to password could be asked.
247       * @param out
248       *          The input stream to used if we have to prompt to the
249       *          user.
250       * @param err
251       *          The error stream to used if we have to prompt to the
252       *          user.
253       * @return The password stored into the specified file on by the
254       *         command line argument, or prompts it if not specified.
255       */
256      public String getBindPassword(String dn, OutputStream out, OutputStream err)
257      {
258        return getBindPassword(dn, out, err, secureArgsList.bindPasswordArg,
259            secureArgsList.bindPasswordFileArg);
260      }
261    
262      /**
263       * Get the password which has to be used for the command without prompting
264       * the user.  If no password was specified, return null.
265       *
266       * @param clearArg
267       *          The password StringArgument argument.
268       * @param fileArg
269       *          The password FileBased argument.
270       * @return The password stored into the specified file on by the
271       *         command line argument, or null it if not specified.
272       */
273      public String getBindPassword(StringArgument clearArg,
274          FileBasedArgument fileArg)
275      {
276        String pwd;
277        if (clearArg.isPresent())
278        {
279          pwd = clearArg.getValue();
280        }
281        else
282        if (fileArg.isPresent())
283        {
284          pwd = fileArg.getValue();
285        }
286        else
287        {
288          pwd = null;
289        }
290        return pwd;
291      }
292    
293      /**
294       * Get the password which has to be used for the command without prompting
295       * the user.  If no password was specified, return null.
296       *
297       * @return The password stored into the specified file on by the
298       *         command line argument, or null it if not specified.
299       */
300      public String getBindPassword()
301      {
302        return getBindPassword(secureArgsList.bindPasswordArg,
303            secureArgsList.bindPasswordFileArg);
304      }
305    
306      /**
307       * Initialize Global option.
308       *
309       * @param outStream
310       *          The output stream used for the usage.
311       * @throws ArgumentException
312       *           If there is a problem with any of the parameters used
313       *           to create this argument.
314       * @return a ArrayList with the options created.
315       */
316      protected LinkedHashSet<Argument> createGlobalArguments(
317          OutputStream outStream)
318      throws ArgumentException
319      {
320        secureArgsList = new SecureConnectionCliArgs();
321        LinkedHashSet<Argument> set = secureArgsList.createGlobalArguments();
322    
323        showUsageArg = new BooleanArgument("showUsage", OPTION_SHORT_HELP,
324            OPTION_LONG_HELP, INFO_DESCRIPTION_SHOWUSAGE.get());
325        setUsageArgument(showUsageArg, outStream);
326        set.add(showUsageArg);
327    
328        verboseArg = new BooleanArgument("verbose", OPTION_SHORT_VERBOSE,
329            OPTION_LONG_VERBOSE, INFO_DESCRIPTION_VERBOSE.get());
330        set.add(verboseArg);
331    
332        propertiesFileArg = new StringArgument("propertiesFilePath",
333            null, OPTION_LONG_PROP_FILE_PATH,
334            false, false, true, INFO_PROP_FILE_PATH_PLACEHOLDER.get(), null, null,
335            INFO_DESCRIPTION_PROP_FILE_PATH.get());
336        setFilePropertiesArgument(propertiesFileArg);
337        set.add(propertiesFileArg);
338    
339        noPropertiesFileArg = new BooleanArgument(
340            "noPropertiesFileArgument", null, OPTION_LONG_NO_PROP_FILE,
341            INFO_DESCRIPTION_NO_PROP_FILE.get());
342        setNoPropertiesFileArgument(noPropertiesFileArg);
343        set.add(noPropertiesFileArg);
344    
345    
346        return set;
347      }
348    
349      /**
350       * Initialize the global options with the provided set of arguments.
351       * @param args the arguments to use to initialize the global options.
352       * @throws ArgumentException if there is a conflict with the provided
353       * arguments.
354       */
355      protected void initializeGlobalArguments(Collection<Argument> args)
356      throws ArgumentException
357      {
358        initializeGlobalArguments(args, null);
359      }
360    
361    
362      /**
363       * Initialize the global options with the provided set of arguments.
364       * @param args the arguments to use to initialize the global options.
365       * @param argGroup to which args will be added
366       * @throws ArgumentException if there is a conflict with the provided
367       * arguments.
368       */
369      protected void initializeGlobalArguments(
370              Collection<Argument> args,
371              ArgumentGroup argGroup)
372      throws ArgumentException
373      {
374    
375        for (Argument arg : args)
376        {
377          addGlobalArgument(arg, argGroup);
378        }
379    
380        // Set the propertiesFile argument
381        setFilePropertiesArgument(propertiesFileArg);
382      }
383    
384      /**
385       * Get the host name which has to be used for the command.
386       *
387       * @return The host name specified by the command line argument, or
388       *         the default value, if not specified.
389       */
390      public String getHostName()
391      {
392        return secureArgsList.getHostName();
393      }
394    
395      /**
396       * Get the port which has to be used for the command.
397       *
398       * @return The port specified by the command line argument, or the
399       *         default value, if not specified.
400       */
401      public String getPort()
402      {
403        return secureArgsList.getPort();
404      }
405    
406      /**
407       * Indication if provided global options are validate.
408       *
409       * @param buf the MessageBuilder to write the error messages.
410       * @return return code.
411       */
412      public int validateGlobalOptions(MessageBuilder buf)
413      {
414        int ret = secureArgsList.validateGlobalOptions(buf) ;
415    
416        // Couldn't have at the same time properties file arg and
417        // propertiesFileArg
418        if (noPropertiesFileArg.isPresent()
419            && propertiesFileArg.isPresent())
420        {
421          Message message = ERR_TOOL_CONFLICTING_ARGS.get(
422              noPropertiesFileArg.getLongIdentifier(), propertiesFileArg
423                  .getLongIdentifier());
424          if (buf.length() > 0)
425          {
426            buf.append(EOL);
427          }
428          buf.append(message);
429          ret = CONFLICTING_ARGS.getReturnCode();
430        }
431    
432        return ret;
433      }
434      /**
435       * Indication if provided global options are validate.
436       *
437       * @param err the stream to be used to print error message.
438       * @return return code.
439       */
440      public int validateGlobalOptions(PrintStream err)
441      {
442        MessageBuilder buf = new MessageBuilder();
443        int returnValue = validateGlobalOptions(buf);
444        if (buf.length() > 0)
445        {
446          err.println(wrapText(buf.toString(), MAX_LINE_WIDTH));
447        }
448        return returnValue;
449      }
450    
451      /**
452       * Indicate if the verbose mode is required.
453       *
454       * @return True if verbose mode is required
455       */
456      public boolean isVerbose()
457      {
458        if (verboseArg.isPresent())
459        {
460          return true;
461        }
462        else
463        {
464          return false ;
465        }
466      }
467    
468    
469      /**
470       * Indicate if the SSL mode is required.
471       *
472       * @return True if SSL mode is required
473       */
474      public boolean useSSL()
475      {
476        return secureArgsList.useSSL();
477      }
478    
479      /**
480       * Indicate if the startTLS mode is required.
481       *
482       * @return True if startTLS mode is required
483       */
484      public boolean useStartTLS()
485      {
486        return secureArgsList.useStartTLS();
487      }
488    
489      /**
490       * Handle TrustStore.
491       *
492       * @return The trustStore manager to be used for the command.
493       */
494      public ApplicationTrustManager getTrustManager()
495      {
496        return secureArgsList.getTrustManager();
497      }
498    
499      /**
500       * Handle KeyStore.
501       *
502       * @return The keyStore manager to be used for the command.
503       */
504      public KeyManager getKeyManager()
505      {
506        return secureArgsList.getKeyManager() ;
507      }
508    }