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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2007-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027 028package org.opends.server.admin.client.cli; 029 030import static com.forgerock.opendj.cli.CliMessages.*; 031import static com.forgerock.opendj.cli.ReturnCode.*; 032import static com.forgerock.opendj.cli.Utils.*; 033 034import java.io.IOException; 035import java.io.OutputStream; 036import java.io.PrintStream; 037import java.util.Collection; 038import java.util.Set; 039 040import org.forgerock.i18n.LocalizableMessage; 041import org.forgerock.i18n.LocalizableMessageBuilder; 042import org.forgerock.i18n.slf4j.LocalizedLogger; 043import org.opends.admin.ads.util.ApplicationTrustManager; 044 045import com.forgerock.opendj.cli.Argument; 046import com.forgerock.opendj.cli.ArgumentException; 047import com.forgerock.opendj.cli.ArgumentGroup; 048import com.forgerock.opendj.cli.BooleanArgument; 049import com.forgerock.opendj.cli.ClientException; 050import com.forgerock.opendj.cli.CommonArguments; 051import com.forgerock.opendj.cli.ConsoleApplication; 052import com.forgerock.opendj.cli.FileBasedArgument; 053import com.forgerock.opendj.cli.StringArgument; 054import com.forgerock.opendj.cli.SubCommandArgumentParser; 055 056/** 057 * This is a commodity class that can be used to check the arguments required to 058 * establish a secure connection in the command line. It can be used to generate 059 * an ApplicationTrustManager object based on the options provided by the user 060 * in the command line. 061 */ 062public abstract class SecureConnectionCliParser extends SubCommandArgumentParser 063{ 064 /** 065 * Logger. 066 */ 067 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 068 069 /** 070 * The 'showUsage' global argument. 071 */ 072 protected BooleanArgument showUsageArg; 073 074 /** 075 * The 'verbose' global argument. 076 */ 077 protected BooleanArgument verboseArg; 078 079 /** 080 * The secure args list object. 081 */ 082 protected SecureConnectionCliArgs secureArgsList ; 083 084 /** 085 * Argument indicating a properties file argument. 086 */ 087 protected StringArgument propertiesFileArg; 088 089 /** 090 * The argument which should be used to indicate that we will not 091 * look for properties file. 092 */ 093 protected BooleanArgument noPropertiesFileArg; 094 095 /** 096 * Creates a new instance of this argument parser with no arguments. 097 * 098 * @param mainClassName 099 * The fully-qualified name of the Java class that should 100 * be invoked to launch the program with which this 101 * argument parser is associated. 102 * @param toolDescription 103 * A human-readable description for the tool, which will be 104 * included when displaying usage information. 105 * @param longArgumentsCaseSensitive 106 * Indicates whether subcommand and long argument names 107 * should be treated in a case-sensitive manner. 108 */ 109 protected SecureConnectionCliParser(String mainClassName, 110 LocalizableMessage toolDescription, boolean longArgumentsCaseSensitive) 111 { 112 super(mainClassName, toolDescription, longArgumentsCaseSensitive); 113 } 114 115 /** 116 * Get the bindDN which has to be used for the command. 117 * 118 * @return The bindDN specified by the command line argument, or the 119 * default value, if not specified. 120 */ 121 public String getBindDN() 122 { 123 return secureArgsList.getBindDN(); 124 } 125 126 127 /** 128 * Returns the Administrator UID provided in the command-line. 129 * @return the Administrator UID provided in the command-line. 130 */ 131 public String getAdministratorUID() 132 { 133 return secureArgsList.getAdministratorUID(); 134 } 135 136 /** 137 * Get the password which has to be used for the command. 138 * 139 * @param dn 140 * The user DN for which to password could be asked. 141 * @param out 142 * The input stream to used if we have to prompt to the 143 * user. 144 * @param err 145 * The error stream to used if we have to prompt to the 146 * user. 147 * @param pwdArg 148 * The password StringArgument argument. 149 * @param fileArg 150 * The password FileBased argument. 151 * @return The password stored into the specified file on by the 152 * command line argument, or prompts it if not specified. 153 */ 154 protected String getBindPassword(String dn, OutputStream out, 155 OutputStream err, StringArgument pwdArg, FileBasedArgument fileArg) 156 { 157 if (fileArg.isPresent()) 158 { 159 return fileArg.getValue(); 160 } 161 162 String bindPasswordValue = pwdArg.isPresent() ? pwdArg.getValue() : null; 163 if (bindPasswordValue == null || "-".equals(bindPasswordValue)) 164 { 165 // Read the password from the STDin. 166 try 167 { 168 return readPassword(dn, out); 169 } 170 catch (Exception ex) 171 { 172 logger.traceException(ex); 173 try 174 { 175 err.write(wrapText(ex.getMessage(), MAX_LINE_WIDTH).getBytes()); 176 err.write(LINE_SEPARATOR.getBytes()); 177 } 178 catch (IOException e) 179 { 180 // Nothing to do. 181 } 182 } 183 } 184 return bindPasswordValue; 185 } 186 187 private String readPassword(String dn, OutputStream out) throws IOException, 188 ClientException 189 { 190 out.write(INFO_LDAPAUTH_PASSWORD_PROMPT.get(dn).toString().getBytes()); 191 out.flush(); 192 char[] pwChars = ConsoleApplication.readPassword(); 193 return new String(pwChars); 194 } 195 196 /** 197 * Gets the password which has to be used for the command. 198 * 199 * @param dn 200 * The user DN for which to password could be asked. 201 * @param out 202 * The input stream to used if we have to prompt to the 203 * user. 204 * @param err 205 * The error stream to used if we have to prompt to the 206 * user. 207 * @return The password stored into the specified file on by the 208 * command line argument, or prompts it if not specified. 209 */ 210 public String getBindPassword(String dn, OutputStream out, OutputStream err) 211 { 212 return getBindPassword(dn, out, err, secureArgsList.bindPasswordArg, 213 secureArgsList.bindPasswordFileArg); 214 } 215 216 /** 217 * Gets the password which has to be used for the command without prompting 218 * the user. If no password was specified, return null. 219 * 220 * @return The password stored into the specified file on by the 221 * command line argument, or null it if not specified. 222 */ 223 public String getBindPassword() 224 { 225 return getBindPassword(secureArgsList.bindPasswordArg, 226 secureArgsList.bindPasswordFileArg); 227 } 228 229 /** 230 * Initialize Global option. 231 * 232 * @param outStream 233 * The output stream used for the usage. 234 * @param alwaysSSL If true, always use the SSL connection type. In this case, 235 * the arguments useSSL and startTLS are not present. 236 * 237 * @throws ArgumentException 238 * If there is a problem with any of the parameters used 239 * to create this argument. 240 * @return a ArrayList with the options created. 241 */ 242 protected Set<Argument> createGlobalArguments(OutputStream outStream, boolean alwaysSSL) throws ArgumentException 243 { 244 secureArgsList = new SecureConnectionCliArgs(alwaysSSL); 245 Set<Argument> set = secureArgsList.createGlobalArguments(); 246 247 showUsageArg = CommonArguments.getShowUsage(); 248 setUsageArgument(showUsageArg, outStream); 249 set.add(showUsageArg); 250 251 verboseArg = CommonArguments.getVerbose(); 252 set.add(verboseArg); 253 254 propertiesFileArg = CommonArguments.getPropertiesFile(); 255 setFilePropertiesArgument(propertiesFileArg); 256 set.add(propertiesFileArg); 257 258 noPropertiesFileArg = CommonArguments.getNoPropertiesFile(); 259 setNoPropertiesFileArgument(noPropertiesFileArg); 260 set.add(noPropertiesFileArg); 261 262 return set; 263 } 264 265 /** 266 * Initialize the global options with the provided set of arguments. 267 * @param args the arguments to use to initialize the global options. 268 * @throws ArgumentException if there is a conflict with the provided 269 * arguments. 270 */ 271 protected void initializeGlobalArguments(Collection<Argument> args) 272 throws ArgumentException 273 { 274 initializeGlobalArguments(args, null); 275 } 276 277 278 /** 279 * Initialize the global options with the provided set of arguments. 280 * @param args the arguments to use to initialize the global options. 281 * @param argGroup to which args will be added 282 * @throws ArgumentException if there is a conflict with the provided 283 * arguments. 284 */ 285 protected void initializeGlobalArguments( 286 Collection<Argument> args, 287 ArgumentGroup argGroup) 288 throws ArgumentException 289 { 290 for (Argument arg : args) 291 { 292 addGlobalArgument(arg, argGroup); 293 } 294 295 // Set the propertiesFile argument 296 setFilePropertiesArgument(propertiesFileArg); 297 } 298 299 /** 300 * Get the host name which has to be used for the command. 301 * 302 * @return The host name specified by the command line argument, or 303 * the default value, if not specified. 304 */ 305 public String getHostName() 306 { 307 return secureArgsList.getHostName(); 308 } 309 310 /** 311 * Get the port which has to be used for the command. 312 * 313 * @return The port specified by the command line argument, or the 314 * default value, if not specified. 315 */ 316 public String getPort() 317 { 318 return secureArgsList.getPort(); 319 } 320 321 /** 322 * Indication if provided global options are validate. 323 * 324 * @param buf the LocalizableMessageBuilder to write the error messages. 325 * @return return code. 326 */ 327 public int validateGlobalOptions(LocalizableMessageBuilder buf) 328 { 329 int ret = secureArgsList.validateGlobalOptions(buf) ; 330 331 // Couldn't have at the same time properties file arg and 332 // propertiesFileArg 333 if (noPropertiesFileArg.isPresent() 334 && propertiesFileArg.isPresent()) 335 { 336 LocalizableMessage message = ERR_TOOL_CONFLICTING_ARGS.get( 337 noPropertiesFileArg.getLongIdentifier(), propertiesFileArg 338 .getLongIdentifier()); 339 if (buf.length() > 0) 340 { 341 buf.append(LINE_SEPARATOR); 342 } 343 buf.append(message); 344 return CONFLICTING_ARGS.get(); 345 } 346 347 return ret; 348 } 349 /** 350 * Indication if provided global options are validate. 351 * 352 * @param err the stream to be used to print error message. 353 * @return return code. 354 */ 355 public int validateGlobalOptions(PrintStream err) 356 { 357 LocalizableMessageBuilder buf = new LocalizableMessageBuilder(); 358 int returnValue = validateGlobalOptions(buf); 359 printWrappedText(err, buf.toString()); 360 return returnValue; 361 } 362 363 /** 364 * Indicate if the verbose mode is required. 365 * 366 * @return True if verbose mode is required 367 */ 368 public boolean isVerbose() 369 { 370 return verboseArg.isPresent(); 371 } 372 373 374 /** 375 * Indicate if the SSL mode is required. 376 * 377 * @return True if SSL mode is required 378 */ 379 public boolean useSSL() 380 { 381 return secureArgsList.useSSL(); 382 } 383 384 /** 385 * Indicate if the startTLS mode is required. 386 * 387 * @return True if startTLS mode is required 388 */ 389 public boolean useStartTLS() 390 { 391 return secureArgsList.useStartTLS(); 392 } 393 394 /** 395 * Handle TrustStore. 396 * 397 * @return The trustStore manager to be used for the command. 398 */ 399 public ApplicationTrustManager getTrustManager() 400 { 401 return secureArgsList.getTrustManager(); 402 } 403 404 /** 405 * Returns the timeout to be used to connect in milliseconds. The method 406 * must be called after parsing the arguments. 407 * @return the timeout to be used to connect in milliseconds. Returns 408 * {@code 0} if there is no timeout. 409 * @throws IllegalStateException if the method is called before 410 * parsing the arguments. 411 */ 412 public int getConnectTimeout()throws IllegalStateException 413 { 414 try 415 { 416 return secureArgsList.connectTimeoutArg.getIntValue(); 417 } 418 catch (ArgumentException ae) 419 { 420 throw new IllegalStateException("Argument parser is not parsed: "+ae, ae); 421 } 422 } 423}