001/*
002 * Copyright 2016-2019 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-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.sdk.unboundidds.tools;
022
023
024
025import java.io.BufferedReader;
026import java.io.File;
027import java.io.FileOutputStream;
028import java.io.FileReader;
029import java.io.OutputStream;
030import java.util.ArrayList;
031import java.util.Arrays;
032import java.util.LinkedHashMap;
033import java.util.concurrent.atomic.AtomicBoolean;
034
035import com.unboundid.ldap.sdk.DN;
036import com.unboundid.ldap.sdk.ExtendedResult;
037import com.unboundid.ldap.sdk.Filter;
038import com.unboundid.ldap.sdk.LDAPConnection;
039import com.unboundid.ldap.sdk.LDAPConnectionOptions;
040import com.unboundid.ldap.sdk.LDAPConnectionPool;
041import com.unboundid.ldap.sdk.LDAPException;
042import com.unboundid.ldap.sdk.ResultCode;
043import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler;
044import com.unboundid.ldap.sdk.Version;
045import com.unboundid.ldif.LDIFWriter;
046import com.unboundid.util.Debug;
047import com.unboundid.util.DNFileReader;
048import com.unboundid.util.LDAPCommandLineTool;
049import com.unboundid.util.FilterFileReader;
050import com.unboundid.util.FixedRateBarrier;
051import com.unboundid.util.RateAdjustor;
052import com.unboundid.util.StaticUtils;
053import com.unboundid.util.ThreadSafety;
054import com.unboundid.util.ThreadSafetyLevel;
055import com.unboundid.util.args.ArgumentException;
056import com.unboundid.util.args.ArgumentParser;
057import com.unboundid.util.args.BooleanArgument;
058import com.unboundid.util.args.BooleanValueArgument;
059import com.unboundid.util.args.DNArgument;
060import com.unboundid.util.args.FileArgument;
061import com.unboundid.util.args.FilterArgument;
062import com.unboundid.util.args.IPAddressArgumentValueValidator;
063import com.unboundid.util.args.IntegerArgument;
064import com.unboundid.util.args.StringArgument;
065import com.unboundid.util.args.TimestampArgument;
066import com.unboundid.util.args.SubCommand;
067
068import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*;
069
070
071
072/**
073 * This class provides a tool that can be used to perform a variety of account
074 * management functions against user entries in the Ping Identity, UnboundID,
075 * or Nokia/Alcatel-Lucent 8661 Directory Server.  It primarily uses the
076 * password policy state extended operation for its processing.
077 * <BR>
078 * <BLOCKQUOTE>
079 *   <B>NOTE:</B>  This class, and other classes within the
080 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
081 *   supported for use against Ping Identity, UnboundID, and
082 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
083 *   for proprietary functionality or for external specifications that are not
084 *   considered stable or mature enough to be guaranteed to work in an
085 *   interoperable way with other types of LDAP servers.
086 * </BLOCKQUOTE>
087 */
088@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE)
089public final class ManageAccount
090       extends LDAPCommandLineTool
091       implements UnsolicitedNotificationHandler
092{
093  /**
094   * The column at which to wrap long lines.
095   */
096  private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1;
097
098
099
100  /**
101   * The primary name of the argument used to indicate that the tool should
102   * append to the reject file rather than overwrite it.
103   */
104  private static final String ARG_APPEND_TO_REJECT_FILE = "appendToRejectFile";
105
106
107
108  /**
109   * The primary name of the argument used to specify a base DN to use for
110   * searches.
111   */
112  static final String ARG_BASE_DN = "baseDN";
113
114
115
116  /**
117   * The primary name of the argument used to specify the path to a file to a
118   * sample variable rate data file to create.
119   */
120  private static final String ARG_GENERATE_SAMPLE_RATE_FILE =
121       "generateSampleRateFile";
122
123
124
125  /**
126   * The primary name of the argument used to specify the path to a file
127   * containing the DNs of the users on which to operate.
128   */
129  private static final String ARG_DN_INPUT_FILE = "dnInputFile";
130
131
132
133  /**
134   * The primary name of the argument used to specify the path to a file
135   * containing search filters to use to identify users.
136   */
137  private static final String ARG_FILTER_INPUT_FILE = "filterInputFile";
138
139
140
141  /**
142   * The primary name of the argument used to specify the number of threads to
143   * use to process search operations to identify which users to target.
144   */
145  static final String ARG_NUM_SEARCH_THREADS = "numSearchThreads";
146
147
148
149  /**
150   * The primary name of the argument used to specify the number of threads to
151   * use to perform manage-account processing.
152   */
153  static final String ARG_NUM_THREADS = "numThreads";
154
155
156
157  /**
158   * The primary name of the argument used to specify the target rate of
159   * operations per second.
160   */
161  private static final String ARG_RATE_PER_SECOND = "ratePerSecond";
162
163
164
165  /**
166   * The primary name of the argument used to specify the path to a reject file
167   * to create.
168   */
169  private static final String ARG_REJECT_FILE = "rejectFile";
170
171
172
173  /**
174   * The primary name of the argument used to specify the simple page size to
175   * use when performing searches.
176   */
177  static final String ARG_SIMPLE_PAGE_SIZE = "simplePageSize";
178
179
180
181  /**
182   * The primary name of the argument used to suppress result operation types
183   * without values.
184   */
185  static final String ARG_SUPPRESS_EMPTY_RESULT_OPERATIONS =
186       "suppressEmptyResultOperations";
187
188
189
190  /**
191   * The primary name of the argument used to specify the DN of the user on
192   * which to operate.
193   */
194  private static final String ARG_TARGET_DN = "targetDN";
195
196
197
198  /**
199   * The primary name of the argument used to specify a search filter to use to
200   * identify users.
201   */
202  private static final String ARG_TARGET_FILTER = "targetFilter";
203
204
205
206  /**
207   * The primary name of the argument used to specify the user IDs of target
208   * users.
209   */
210  private static final String ARG_TARGET_USER_ID = "targetUserID";
211
212
213
214  /**
215   * The primary name of the argument used to specify the name of the attribute
216   * to identify which user has a given user ID.
217   */
218  static final String ARG_USER_ID_ATTRIBUTE = "userIDAttribute";
219
220
221
222  /**
223   * The primary name of the argument used to specify the path to a file
224   * containing the user IDs of the target users.
225   */
226  private static final String ARG_USER_ID_INPUT_FILE = "userIDInputFile";
227
228
229
230  /**
231   * The primary name of the argument used to specify the path to a variable
232   * rate data file.
233   */
234  private static final String ARG_VARIABLE_RATE_DATA = "variableRateData";
235
236
237
238  /**
239   * The default search base DN.
240   */
241  private static final DN DEFAULT_BASE_DN = DN.NULL_DN;
242
243
244
245  /**
246   * The default user ID attribute.
247   */
248  private static final String DEFAULT_USER_ID_ATTRIBUTE = "uid";
249
250
251
252  /**
253   * A target user DN to use in examples.
254   */
255  private static final String EXAMPLE_TARGET_USER_DN =
256       "uid=jdoe,ou=People,dc=example,dc=com";
257
258
259
260  // The argument parser for this tool.
261  private volatile ArgumentParser parser;
262
263  // Indicates whether all DNs have been provided to the manage-account
264  // processor.
265  private final AtomicBoolean allDNsProvided;
266
267  // Indicates whether all filters have been provided to the manage-account
268  // search processor.
269  private final AtomicBoolean allFiltersProvided;
270
271  // Indicates whether a request has been made to cancel processing.
272  private final AtomicBoolean cancelRequested;
273
274  // The rate limiter to use for this tool.
275  private volatile FixedRateBarrier rateLimiter;
276
277  // The LDAP connection options to use for connections created by this tool.
278  private final LDAPConnectionOptions connectionOptions;
279
280  // The LDIF writer to use to write information about successful and failed
281  // operations.
282  private volatile LDIFWriter outputWriter;
283
284  // The LDIF writer to use to write information about failed operations.
285  private volatile LDIFWriter rejectWriter;
286
287  // The search processor for this tool.
288  private volatile ManageAccountSearchProcessor searchProcessor;
289
290  // The rate adjustor to use to vary the load over time.
291  private volatile RateAdjustor rateAdjustor;
292
293
294
295  /**
296   * Invokes the tool with the provided set of arguments.
297   *
298   * @param  args  The command-line arguments provided to this tool.
299   */
300  public static void main(final String... args)
301  {
302    final ResultCode resultCode = main(System.out, System.err, args);
303    if (resultCode != ResultCode.SUCCESS)
304    {
305      System.exit(resultCode.intValue());
306    }
307  }
308
309
310
311  /**
312   * Invokes the tool with the provided set of arguments.
313   *
314   * @param  out   The output stream to use for standard out.  It may be
315   *               {@code null} if standard out should be suppressed.
316   * @param  err   The output stream to use for standard error.  It may be
317   *               {@code null} if standard error should be suppressed.
318   * @param  args  The command-line arguments provided to this tool.
319   *
320   * @return  A result code with the status of the tool processing.  Any result
321   *          code other than {@link ResultCode#SUCCESS} should be considered a
322   *          failure.
323   */
324  public static ResultCode main(final OutputStream out, final OutputStream err,
325                                final String... args)
326  {
327    final ManageAccount tool = new ManageAccount(out, err);
328
329    final boolean origCommentAboutBase64EncodedValues =
330         LDIFWriter.commentAboutBase64EncodedValues();
331    LDIFWriter.setCommentAboutBase64EncodedValues(true);
332    try
333    {
334      return tool.runTool(args);
335    }
336    finally
337    {
338      LDIFWriter.setCommentAboutBase64EncodedValues(
339           origCommentAboutBase64EncodedValues);
340    }
341  }
342
343
344
345  /**
346   * Creates a new instance of this tool with the provided arguments.
347   *
348   * @param  out  The output stream to use for standard out.  It may be
349   *              {@code null} if standard out should be suppressed.
350   * @param  err  The output stream to use for standard error.  It may be
351   *              {@code null} if standard error should be suppressed.
352   */
353  public ManageAccount(final OutputStream out, final OutputStream err)
354  {
355    super(out, err);
356
357    connectionOptions = new LDAPConnectionOptions();
358    connectionOptions.setUnsolicitedNotificationHandler(this);
359
360    allDNsProvided = new AtomicBoolean(false);
361    allFiltersProvided = new AtomicBoolean(false);
362    cancelRequested = new AtomicBoolean(false);
363
364    parser = null;
365    rateLimiter = null;
366    rateAdjustor = null;
367    outputWriter = null;
368    rejectWriter = null;
369    searchProcessor = null;
370  }
371
372
373
374  /**
375   * {@inheritDoc}
376   */
377  @Override()
378  public String getToolName()
379  {
380    return "manage-account";
381  }
382
383
384
385  /**
386   * {@inheritDoc}
387   */
388  @Override()
389  public String getToolDescription()
390  {
391    return INFO_MANAGE_ACCT_TOOL_DESC.get();
392  }
393
394
395
396  /**
397   * {@inheritDoc}
398   */
399  @Override()
400  public String getToolVersion()
401  {
402    return Version.NUMERIC_VERSION_STRING;
403  }
404
405
406
407  /**
408   * {@inheritDoc}
409   */
410  @Override()
411  public boolean supportsInteractiveMode()
412  {
413    return true;
414  }
415
416
417
418  /**
419   * {@inheritDoc}
420   */
421  @Override()
422  public boolean defaultsToInteractiveMode()
423  {
424    return true;
425  }
426
427
428
429  /**
430   * {@inheritDoc}
431   */
432  @Override()
433  public boolean supportsPropertiesFile()
434  {
435    return true;
436  }
437
438
439
440  /**
441   * {@inheritDoc}
442   */
443  @Override()
444  protected boolean supportsOutputFile()
445  {
446    return true;
447  }
448
449
450
451  /**
452   * {@inheritDoc}
453   */
454  @Override()
455  protected boolean supportsAuthentication()
456  {
457    return true;
458  }
459
460
461
462  /**
463   * {@inheritDoc}
464   */
465  @Override()
466  protected boolean defaultToPromptForBindPassword()
467  {
468    return true;
469  }
470
471
472
473  /**
474   * {@inheritDoc}
475   */
476  @Override()
477  protected boolean supportsSASLHelp()
478  {
479    return true;
480  }
481
482
483
484  /**
485   * {@inheritDoc}
486   */
487  @Override()
488  protected boolean includeAlternateLongIdentifiers()
489  {
490    return true;
491  }
492
493
494
495  /**
496   * {@inheritDoc}
497   */
498  @Override()
499  protected boolean supportsMultipleServers()
500  {
501    return true;
502  }
503
504
505
506  /**
507   * {@inheritDoc}
508   */
509  @Override()
510  protected boolean logToolInvocationByDefault()
511  {
512    return true;
513  }
514
515
516
517  /**
518   * {@inheritDoc}
519   */
520  @Override()
521  public void addNonLDAPArguments(final ArgumentParser parser)
522       throws ArgumentException
523  {
524    // Get a copy of the argument parser for later use.
525    this.parser = parser;
526
527
528    // Get the current time formatted as a generalized time.
529    final String currentGeneralizedTime =
530         StaticUtils.encodeGeneralizedTime(System.currentTimeMillis());
531    final String olderGeneralizedTime =
532         StaticUtils.encodeGeneralizedTime(
533              System.currentTimeMillis() - 12_345L);
534
535
536    // Define the global arguments used to indicate which users to target.
537    final DNArgument targetDN = new DNArgument('b', ARG_TARGET_DN, false, 0,
538         null, INFO_MANAGE_ACCT_ARG_DESC_TARGET_DN.get());
539    targetDN.addLongIdentifier("userDN", true);
540    targetDN.addLongIdentifier("target-dn", true);
541    targetDN.addLongIdentifier("user-dn", true);
542    targetDN.setArgumentGroupName(
543         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
544    parser.addArgument(targetDN);
545
546    final FileArgument dnInputFile = new FileArgument(null, ARG_DN_INPUT_FILE,
547         false, 0, null, INFO_MANAGE_ACCT_ARG_DESC_DN_FILE.get(), true,
548         true, true, false);
549    dnInputFile.addLongIdentifier("targetDNFile", true);
550    dnInputFile.addLongIdentifier("userDNFile", true);
551    dnInputFile.addLongIdentifier("dn-input-file", true);
552    dnInputFile.addLongIdentifier("target-dn-file", true);
553    dnInputFile.addLongIdentifier("user-dn-file", true);
554    dnInputFile.setArgumentGroupName(
555         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
556    parser.addArgument(dnInputFile);
557
558    final FilterArgument targetFilter = new FilterArgument(null,
559         ARG_TARGET_FILTER, false, 0, null,
560         INFO_MANAGE_ACCT_ARG_DESC_TARGET_FILTER.get(ARG_BASE_DN));
561    targetFilter.addLongIdentifier("target-filter", true);
562    targetFilter.setArgumentGroupName(
563         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
564    parser.addArgument(targetFilter);
565
566    final FileArgument filterInputFile = new FileArgument(null,
567         ARG_FILTER_INPUT_FILE, false, 0, null,
568         INFO_MANAGE_ACCT_ARG_DESC_FILTER_INPUT_FILE.get(ARG_BASE_DN),
569         true, true, true, false);
570    filterInputFile.addLongIdentifier("targetFilterFile", true);
571    filterInputFile.addLongIdentifier("filter-input-file", true);
572    filterInputFile.addLongIdentifier("target-filter-file", true);
573    filterInputFile.setArgumentGroupName(
574         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
575    parser.addArgument(filterInputFile);
576
577    final StringArgument targetUserID = new StringArgument(null,
578         ARG_TARGET_USER_ID, false, 0, null,
579         INFO_MANAGE_ACCT_ARG_DESC_TARGET_USER_ID.get(ARG_BASE_DN,
580              ARG_USER_ID_ATTRIBUTE));
581    targetUserID.addLongIdentifier("userID", true);
582    targetUserID.addLongIdentifier("target-user-id", true);
583    targetUserID.addLongIdentifier("user-id", true);
584    targetUserID.setArgumentGroupName(
585         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
586    parser.addArgument(targetUserID);
587
588    final FileArgument userIDInputFile = new FileArgument(null,
589         ARG_USER_ID_INPUT_FILE, false, 0, null,
590         INFO_MANAGE_ACCT_ARG_DESC_USER_ID_INPUT_FILE.get(ARG_BASE_DN,
591              ARG_USER_ID_ATTRIBUTE),
592         true, true, true, false);
593    userIDInputFile.addLongIdentifier("targetUserIDFile", true);
594    userIDInputFile.addLongIdentifier("user-id-input-file", true);
595    userIDInputFile.addLongIdentifier("target-user-id-file", true);
596    userIDInputFile.setArgumentGroupName(
597         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
598    parser.addArgument(userIDInputFile);
599
600    final StringArgument userIDAttribute = new StringArgument(null,
601         ARG_USER_ID_ATTRIBUTE, false, 1, null,
602         INFO_MANAGE_ACCT_ARG_DESC_USER_ID_ATTR.get(
603              ARG_TARGET_USER_ID, ARG_USER_ID_INPUT_FILE,
604              DEFAULT_USER_ID_ATTRIBUTE),
605         DEFAULT_USER_ID_ATTRIBUTE);
606    userIDAttribute.addLongIdentifier("user-id-attribute", true);
607    userIDAttribute.setArgumentGroupName(
608         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
609    parser.addArgument(userIDAttribute);
610
611    final DNArgument baseDN = new DNArgument(null, ARG_BASE_DN, false, 1, null,
612         INFO_MANAGE_ACCT_ARG_DESC_BASE_DN.get(ARG_TARGET_FILTER,
613              ARG_FILTER_INPUT_FILE, ARG_TARGET_USER_ID,
614              ARG_USER_ID_INPUT_FILE),
615         DEFAULT_BASE_DN);
616    baseDN.addLongIdentifier("base-dn", true);
617    baseDN.setArgumentGroupName(
618         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get());
619    parser.addArgument(baseDN);
620
621    final IntegerArgument simplePageSize = new IntegerArgument('z',
622         ARG_SIMPLE_PAGE_SIZE, false, 1, null,
623         INFO_MANAGE_ACCT_ARG_DESC_SIMPLE_PAGE_SIZE.get(getToolName()), 1,
624         Integer.MAX_VALUE);
625    simplePageSize.addLongIdentifier("simple-page-size", true);
626    simplePageSize.setArgumentGroupName(
627         INFO_MANAGE_ACCT_ARG_GROUP_TARGET_USER_ARGS.get(getToolName()));
628    parser.addArgument(simplePageSize);
629
630
631    // Ensure that the user will be required ot provide at least one of the
632    // arguments to specify which users to target.
633    parser.addRequiredArgumentSet(targetDN, dnInputFile, targetFilter,
634         filterInputFile, targetUserID, userIDInputFile);
635
636
637    // Define the global arguments used to control the amount of load the tool
638    // should be permitted to generate.
639    final IntegerArgument numThreads = new IntegerArgument('t', ARG_NUM_THREADS,
640         false, 1, null,
641         INFO_MANAGE_ACCT_ARG_DESC_NUM_THREADS.get(getToolName()), 1,
642         Integer.MAX_VALUE, 1);
643    numThreads.addLongIdentifier("num-threads", true);
644    numThreads.setArgumentGroupName(
645         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
646    parser.addArgument(numThreads);
647
648    final IntegerArgument numSearchThreads = new IntegerArgument(null,
649         ARG_NUM_SEARCH_THREADS, false, 1, null,
650         INFO_MANAGE_ACCT_ARG_DESC_NUM_SEARCH_THREADS.get(getToolName()), 1,
651         Integer.MAX_VALUE, 1);
652    numSearchThreads.addLongIdentifier("num-search-threads", true);
653    numSearchThreads.setArgumentGroupName(
654         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
655    parser.addArgument(numSearchThreads);
656
657    final IntegerArgument ratePerSecond = new IntegerArgument('r',
658         ARG_RATE_PER_SECOND, false, 1, null,
659         INFO_MANAGE_ACCT_ARG_DESC_RATE_PER_SECOND.get(
660              ARG_VARIABLE_RATE_DATA),
661         1, Integer.MAX_VALUE);
662    ratePerSecond.addLongIdentifier("rate-per-second", true);
663    ratePerSecond.setArgumentGroupName(
664         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
665    parser.addArgument(ratePerSecond);
666
667    final FileArgument variableRateData = new FileArgument(null,
668         ARG_VARIABLE_RATE_DATA, false, 1, null,
669         INFO_MANAGE_ACCT_ARG_DESC_VARIABLE_RATE_DATA.get(
670              ARG_RATE_PER_SECOND),
671         true, true, true, false);
672    variableRateData.addLongIdentifier("variable-rate-data", true);
673    variableRateData.setArgumentGroupName(
674         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
675    parser.addArgument(variableRateData);
676
677    final FileArgument generateSampleRateFile = new FileArgument(null,
678         ARG_GENERATE_SAMPLE_RATE_FILE, false, 1, null,
679         INFO_MANAGE_ACCT_ARG_DESC_GENERATE_SAMPLE_RATE_FILE.get(
680              ARG_VARIABLE_RATE_DATA),
681         false, true, true, false);
682    generateSampleRateFile.addLongIdentifier("generate-sample-rate-file", true);
683    generateSampleRateFile.setArgumentGroupName(
684         INFO_MANAGE_ACCT_ARG_GROUP_PERFORMANCE.get());
685    generateSampleRateFile.setUsageArgument(true);
686    parser.addArgument(generateSampleRateFile);
687
688
689    // Define the global arguments tht pertain to the reject file.
690    final FileArgument rejectFile = new FileArgument('R', ARG_REJECT_FILE,
691         false, 1, null, INFO_MANAGE_ACCT_ARG_DESC_REJECT_FILE.get(),
692         false, true, true, false);
693    rejectFile.addLongIdentifier("reject-file", true);
694    parser.addArgument(rejectFile);
695
696    final BooleanArgument appendToRejectFile = new BooleanArgument(null,
697         ARG_APPEND_TO_REJECT_FILE, 1,
698         INFO_MANAGE_ACCT_ARG_DESC_APPEND_TO_REJECT_FILE.get(
699              rejectFile.getIdentifierString()));
700    appendToRejectFile.addLongIdentifier("append-to-reject-file", true);
701    parser.addArgument(appendToRejectFile);
702
703    parser.addDependentArgumentSet(appendToRejectFile, rejectFile);
704
705
706    // Define the argument used to suppress result operations without values.
707    final BooleanArgument suppressEmptyResultOperations =
708         new BooleanArgument(null, ARG_SUPPRESS_EMPTY_RESULT_OPERATIONS,
709              1,
710              INFO_MANAGE_ACCT_ARG_DESC_SUPPRESS_EMPTY_RESULT_OPERATIONS.get(
711                   getToolName()));
712    parser.addArgument(suppressEmptyResultOperations);
713
714
715    // Define the subcommand used to retrieve all state information for a user.
716    createSubCommand(ManageAccountSubCommandType.GET_ALL,
717         INFO_MANAGE_ACCT_SC_GET_ALL_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
718
719
720    // Define the subcommand used to retrieve the password policy DN for a user.
721    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_POLICY_DN,
722         INFO_MANAGE_ACCT_SC_GET_POLICY_DN_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
723
724
725    // Define the subcommand to determine whether the account is usable.
726    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_USABLE,
727         INFO_MANAGE_ACCT_SC_GET_IS_USABLE_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
728
729
730    // Define the subcommand to retrieve the set of password policy state
731    // account usability notice messages.
732    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_NOTICES,
733         INFO_MANAGE_ACCT_SC_GET_USABILITY_NOTICES_EXAMPLE.get(
734              EXAMPLE_TARGET_USER_DN));
735
736
737    // Define the subcommand to retrieve the set of password policy state
738    // account usability warning messages.
739    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_WARNINGS,
740         INFO_MANAGE_ACCT_SC_GET_USABILITY_WARNINGS_EXAMPLE.get(
741              EXAMPLE_TARGET_USER_DN));
742
743
744    // Define the subcommand to retrieve the set of password policy state
745    // account usability error messages.
746    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_ERRORS,
747         INFO_MANAGE_ACCT_SC_GET_USABILITY_ERRORS_EXAMPLE.get(
748              EXAMPLE_TARGET_USER_DN));
749
750
751    // Define the subcommand to retrieve the password changed time for a user.
752    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_CHANGED_TIME,
753         INFO_MANAGE_ACCT_SC_GET_PW_CHANGED_TIME_EXAMPLE.get(
754              EXAMPLE_TARGET_USER_DN));
755
756
757    // Define the subcommand to set the password changed time for a user.
758    final ArgumentParser setPWChangedTimeParser =
759         createSubCommandParser(
760              ManageAccountSubCommandType.SET_PASSWORD_CHANGED_TIME);
761
762    final TimestampArgument setPWChangedTimeValueArg = new TimestampArgument(
763         'O', "passwordChangedTime", false, 1, null,
764         INFO_MANAGE_ACCT_SC_SET_PW_CHANGED_TIME_ARG_VALUE.get());
765    setPWChangedTimeValueArg.addLongIdentifier("operationValue", true);
766    setPWChangedTimeValueArg.addLongIdentifier("password-changed-time", true);
767    setPWChangedTimeValueArg.addLongIdentifier("operation-value", true);
768    setPWChangedTimeParser.addArgument(setPWChangedTimeValueArg);
769
770    createSubCommand(ManageAccountSubCommandType.SET_PASSWORD_CHANGED_TIME,
771         setPWChangedTimeParser,
772         createSubCommandExample(
773              ManageAccountSubCommandType.SET_PASSWORD_CHANGED_TIME,
774              INFO_MANAGE_ACCT_SC_SET_PW_CHANGED_TIME_EXAMPLE.get(
775                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
776              "--passwordChangedTime", currentGeneralizedTime));
777
778
779    // Define the subcommand to clear the password changed time for a user.
780    createSubCommand(ManageAccountSubCommandType.CLEAR_PASSWORD_CHANGED_TIME,
781         INFO_MANAGE_ACCT_SC_CLEAR_PW_CHANGED_TIME_EXAMPLE.get(
782              EXAMPLE_TARGET_USER_DN));
783
784
785    // Define the subcommand to determine whether a user account is disabled.
786    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_DISABLED,
787         INFO_MANAGE_ACCT_SC_GET_IS_DISABLED_EXAMPLE.get(
788              EXAMPLE_TARGET_USER_DN));
789
790
791    // Define the subcommand to specify whether a user's account is disabled.
792    final ArgumentParser setAcctDisabledParser =
793         createSubCommandParser(
794              ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED);
795
796    final BooleanValueArgument setAcctDisabledValueArg =
797         new BooleanValueArgument('O', "accountIsDisabled", true, null,
798              INFO_MANAGE_ACCT_SC_SET_IS_DISABLED_ARG_VALUE.get());
799    setAcctDisabledValueArg.addLongIdentifier("operationValue", true);
800    setAcctDisabledValueArg.addLongIdentifier("account-is-disabled", true);
801    setAcctDisabledValueArg.addLongIdentifier("operation-value", true);
802    setAcctDisabledParser.addArgument(setAcctDisabledValueArg);
803
804    createSubCommand(ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED,
805         setAcctDisabledParser,
806         createSubCommandExample(
807              ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED,
808              INFO_MANAGE_ACCT_SC_SET_IS_DISABLED_EXAMPLE.get(
809                   EXAMPLE_TARGET_USER_DN),
810              "--accountIsDisabled", "true"));
811
812
813    // Define the subcommand to clear the account disabled state.
814    createSubCommand(ManageAccountSubCommandType.CLEAR_ACCOUNT_IS_DISABLED,
815         INFO_MANAGE_ACCT_SC_CLEAR_IS_DISABLED_EXAMPLE.get(
816              EXAMPLE_TARGET_USER_DN));
817
818
819    // Define the subcommand to retrieve the account activation time for a user.
820    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_ACTIVATION_TIME,
821         INFO_MANAGE_ACCT_SC_GET_ACCT_ACT_TIME_EXAMPLE.get(
822              EXAMPLE_TARGET_USER_DN));
823
824
825    // Define the subcommand to set the account activation time for a user.
826    final ArgumentParser setAcctActivationTimeParser =
827         createSubCommandParser(
828              ManageAccountSubCommandType.SET_ACCOUNT_ACTIVATION_TIME);
829
830    final TimestampArgument setAcctActivationTimeValueArg =
831         new TimestampArgument('O', "accountActivationTime", false, 1, null,
832              INFO_MANAGE_ACCT_SC_SET_ACCT_ACT_TIME_ARG_VALUE.get());
833    setAcctActivationTimeValueArg.addLongIdentifier("operationValue", true);
834    setAcctActivationTimeValueArg.addLongIdentifier("account-activation-time",
835         true);
836    setAcctActivationTimeValueArg.addLongIdentifier("operation-value", true);
837    setAcctActivationTimeParser.addArgument(setAcctActivationTimeValueArg);
838
839    createSubCommand(ManageAccountSubCommandType.SET_ACCOUNT_ACTIVATION_TIME,
840         setAcctActivationTimeParser,
841         createSubCommandExample(
842              ManageAccountSubCommandType.SET_ACCOUNT_ACTIVATION_TIME,
843              INFO_MANAGE_ACCT_SC_SET_ACCT_ACT_TIME_EXAMPLE.get(
844                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
845              "--accountActivationTime", currentGeneralizedTime));
846
847
848    // Define the subcommand to clear the account activation time for a user.
849    createSubCommand(ManageAccountSubCommandType.CLEAR_ACCOUNT_ACTIVATION_TIME,
850         INFO_MANAGE_ACCT_SC_CLEAR_ACCT_ACT_TIME_EXAMPLE.get(
851              EXAMPLE_TARGET_USER_DN));
852
853
854    // Define the subcommand to retrieve the length of time until a user's
855    // account is activated.
856    createSubCommand(
857         ManageAccountSubCommandType.GET_SECONDS_UNTIL_ACCOUNT_ACTIVATION,
858         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_ACCT_ACT_EXAMPLE.get(
859              EXAMPLE_TARGET_USER_DN));
860
861
862    // Define the subcommand to determine whether a user's account is not yet
863    // active.
864    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_NOT_YET_ACTIVE,
865         INFO_MANAGE_ACCT_SC_GET_ACCT_NOT_YET_ACTIVE_EXAMPLE.get(
866              EXAMPLE_TARGET_USER_DN));
867
868
869    // Define the subcommand to retrieve the account expiration time for a user.
870    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_EXPIRATION_TIME,
871         INFO_MANAGE_ACCT_SC_GET_ACCT_EXP_TIME_EXAMPLE.get(
872              EXAMPLE_TARGET_USER_DN));
873
874
875    // Define the subcommand to set the account expiration time for a user.
876    final ArgumentParser setAcctExpirationTimeParser =
877         createSubCommandParser(
878              ManageAccountSubCommandType.SET_ACCOUNT_EXPIRATION_TIME);
879
880    final TimestampArgument setAcctExpirationTimeValueArg =
881         new TimestampArgument('O', "accountExpirationTime", false, 1, null,
882              INFO_MANAGE_ACCT_SC_SET_ACCT_EXP_TIME_ARG_VALUE.get());
883    setAcctExpirationTimeValueArg.addLongIdentifier("operationValue", true);
884    setAcctExpirationTimeValueArg.addLongIdentifier("account-expiration-time",
885         true);
886    setAcctExpirationTimeValueArg.addLongIdentifier("operation-value", true);
887    setAcctExpirationTimeParser.addArgument(setAcctExpirationTimeValueArg);
888
889    createSubCommand(ManageAccountSubCommandType.SET_ACCOUNT_EXPIRATION_TIME,
890         setAcctExpirationTimeParser,
891         createSubCommandExample(
892              ManageAccountSubCommandType.SET_ACCOUNT_EXPIRATION_TIME,
893              INFO_MANAGE_ACCT_SC_SET_ACCT_EXP_TIME_EXAMPLE.get(
894                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
895              "--accountExpirationTime", currentGeneralizedTime));
896
897
898    // Define the subcommand to clear the account expiration time for a user.
899    createSubCommand(ManageAccountSubCommandType.CLEAR_ACCOUNT_EXPIRATION_TIME,
900         INFO_MANAGE_ACCT_SC_CLEAR_ACCT_EXP_TIME_EXAMPLE.get(
901              EXAMPLE_TARGET_USER_DN));
902
903
904    // Define the subcommand to retrieve the length of time until a user's
905    // account is expired.
906    createSubCommand(
907         ManageAccountSubCommandType.GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION,
908         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_ACCT_EXP_EXAMPLE.get(
909              EXAMPLE_TARGET_USER_DN));
910
911
912    // Define the subcommand to determine whether a user's account is expired.
913    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_EXPIRED,
914         INFO_MANAGE_ACCT_SC_GET_ACCT_IS_EXPIRED_EXAMPLE.get(
915              EXAMPLE_TARGET_USER_DN));
916
917
918    // Define the subcommand to retrieve a user's password expiration warned
919    // time.
920    createSubCommand(
921         ManageAccountSubCommandType.GET_PASSWORD_EXPIRATION_WARNED_TIME,
922         INFO_MANAGE_ACCT_SC_GET_PW_EXP_WARNED_TIME_EXAMPLE.get(
923              EXAMPLE_TARGET_USER_DN));
924
925
926    // Define the subcommand to set a user's password expiration warned time.
927    final ArgumentParser setPWExpWarnedTimeParser =
928         createSubCommandParser(
929              ManageAccountSubCommandType.SET_PASSWORD_EXPIRATION_WARNED_TIME);
930
931    final TimestampArgument setPWExpWarnedTimeValueArg =
932         new TimestampArgument('O', "passwordExpirationWarnedTime", false, 1,
933              null, INFO_MANAGE_ACCT_SC_SET_PW_EXP_WARNED_TIME_ARG_VALUE.get());
934    setPWExpWarnedTimeValueArg.addLongIdentifier("operationValue", true);
935    setPWExpWarnedTimeValueArg.addLongIdentifier(
936         "password-expiration-warned-time", true);
937    setPWExpWarnedTimeValueArg.addLongIdentifier("operation-value", true);
938    setPWExpWarnedTimeParser.addArgument(setPWExpWarnedTimeValueArg);
939
940    createSubCommand(
941         ManageAccountSubCommandType.SET_PASSWORD_EXPIRATION_WARNED_TIME,
942         setPWExpWarnedTimeParser,
943         createSubCommandExample(
944              ManageAccountSubCommandType.SET_PASSWORD_EXPIRATION_WARNED_TIME,
945              INFO_MANAGE_ACCT_SC_SET_PW_EXP_WARNED_TIME_EXAMPLE.get(
946                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
947              "--passwordExpirationWarnedTime", currentGeneralizedTime));
948
949
950    // Define the subcommand to clear a user's password expiration warned time.
951    createSubCommand(
952         ManageAccountSubCommandType.CLEAR_PASSWORD_EXPIRATION_WARNED_TIME,
953         INFO_MANAGE_ACCT_SC_CLEAR_PW_EXP_WARNED_TIME_EXAMPLE.get(
954              EXAMPLE_TARGET_USER_DN));
955
956
957    // Define the subcommand to get the number of seconds until a user is
958    // eligible to receive a password expiration warning.
959    createSubCommand(
960         ManageAccountSubCommandType.
961              GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING,
962         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_PW_EXP_WARNING_EXAMPLE.get(
963              EXAMPLE_TARGET_USER_DN));
964
965
966    // Define the subcommand to retrieve a user's password expiration time.
967    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_EXPIRATION_TIME,
968         INFO_MANAGE_ACCT_SC_GET_PW_EXP_TIME_EXAMPLE.get(
969              EXAMPLE_TARGET_USER_DN));
970
971
972    // Define the subcommand to get the number of seconds until a user's
973    // password expires.
974    createSubCommand(
975         ManageAccountSubCommandType.GET_SECONDS_UNTIL_PASSWORD_EXPIRATION,
976         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_PW_EXP_EXAMPLE.get(
977              EXAMPLE_TARGET_USER_DN));
978
979
980    // Define the subcommand to determine whether a user's password is expired.
981    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_IS_EXPIRED,
982         INFO_MANAGE_ACCT_SC_GET_PW_IS_EXPIRED_EXAMPLE.get(
983              EXAMPLE_TARGET_USER_DN));
984
985
986    // Define the subcommand to determine whether an account is failure locked.
987    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_FAILURE_LOCKED,
988         INFO_MANAGE_ACCT_SC_GET_ACCT_FAILURE_LOCKED_EXAMPLE.get(
989              EXAMPLE_TARGET_USER_DN));
990
991
992    // Define the subcommand to specify whether an account is failure locked.
993    final ArgumentParser setIsFailureLockedParser =
994         createSubCommandParser(
995              ManageAccountSubCommandType.SET_ACCOUNT_IS_FAILURE_LOCKED);
996
997    final BooleanValueArgument setIsFailureLockedValueArg =
998         new BooleanValueArgument('O', "accountIsFailureLocked", true, null,
999              INFO_MANAGE_ACCT_SC_SET_ACCT_FAILURE_LOCKED_ARG_VALUE.get());
1000    setIsFailureLockedValueArg.addLongIdentifier("operationValue", true);
1001    setIsFailureLockedValueArg.addLongIdentifier("account-is-failure-locked",
1002         true);
1003    setIsFailureLockedValueArg.addLongIdentifier("operation-value", true);
1004    setIsFailureLockedParser.addArgument(setIsFailureLockedValueArg);
1005
1006    createSubCommand(ManageAccountSubCommandType.SET_ACCOUNT_IS_FAILURE_LOCKED,
1007         setIsFailureLockedParser,
1008         createSubCommandExample(
1009              ManageAccountSubCommandType.SET_ACCOUNT_IS_FAILURE_LOCKED,
1010              INFO_MANAGE_ACCT_SC_SET_ACCT_FAILURE_LOCKED_EXAMPLE.get(
1011                   EXAMPLE_TARGET_USER_DN),
1012              "--accountIsFailureLocked", "true"));
1013
1014
1015    // Define the subcommand to get the time an account was failure locked.
1016    createSubCommand(ManageAccountSubCommandType.GET_FAILURE_LOCKOUT_TIME,
1017         INFO_MANAGE_ACCT_SC_GET_FAILURE_LOCKED_TIME_EXAMPLE.get(
1018              EXAMPLE_TARGET_USER_DN));
1019
1020
1021    // Define the subcommand to get the length of time until a failure-locked
1022    // account will be automatically unlocked.
1023    createSubCommand(
1024         ManageAccountSubCommandType.
1025              GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK,
1026         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_FAILURE_UNLOCK_EXAMPLE.get(
1027              EXAMPLE_TARGET_USER_DN));
1028
1029
1030    // Define the subcommand to determine the authentication failure times.
1031    createSubCommand(
1032         ManageAccountSubCommandType.GET_AUTHENTICATION_FAILURE_TIMES,
1033         INFO_MANAGE_ACCT_SC_GET_AUTH_FAILURE_TIMES_EXAMPLE.get(
1034              EXAMPLE_TARGET_USER_DN));
1035
1036
1037    // Define the subcommand to add values to the set of authentication failure
1038    // times.
1039    final ArgumentParser addAuthFailureTimeParser =
1040         createSubCommandParser(
1041              ManageAccountSubCommandType.ADD_AUTHENTICATION_FAILURE_TIME);
1042
1043    final TimestampArgument addAuthFailureTimeValueArg =
1044         new TimestampArgument('O', "authenticationFailureTime", false, 0, null,
1045              INFO_MANAGE_ACCT_SC_ADD_AUTH_FAILURE_TIME_ARG_VALUE.get());
1046    addAuthFailureTimeValueArg.addLongIdentifier("operationValue", true);
1047    addAuthFailureTimeValueArg.addLongIdentifier(
1048         "authentication-failure-time", true);
1049    addAuthFailureTimeValueArg.addLongIdentifier("operation-value", true);
1050    addAuthFailureTimeParser.addArgument(addAuthFailureTimeValueArg);
1051
1052    createSubCommand(
1053         ManageAccountSubCommandType.ADD_AUTHENTICATION_FAILURE_TIME,
1054         addAuthFailureTimeParser,
1055         createSubCommandExample(
1056              ManageAccountSubCommandType.ADD_AUTHENTICATION_FAILURE_TIME,
1057              INFO_MANAGE_ACCT_SC_ADD_AUTH_FAILURE_TIME_EXAMPLE.get(
1058                   EXAMPLE_TARGET_USER_DN)));
1059
1060
1061    // Define the subcommand to replace the authentication failure times.
1062    final ArgumentParser setAuthFailureTimesParser =
1063         createSubCommandParser(
1064              ManageAccountSubCommandType.SET_AUTHENTICATION_FAILURE_TIMES);
1065
1066    final TimestampArgument setAuthFailureTimesValueArg =
1067         new TimestampArgument('O', "authenticationFailureTime", false, 0, null,
1068              INFO_MANAGE_ACCT_SC_SET_AUTH_FAILURE_TIMES_ARG_VALUE.get());
1069    setAuthFailureTimesValueArg.addLongIdentifier("operationValue", true);
1070    setAuthFailureTimesValueArg.addLongIdentifier(
1071         "authentication-failure-time", true);
1072    setAuthFailureTimesValueArg.addLongIdentifier("operation-value", true);
1073    setAuthFailureTimesParser.addArgument(setAuthFailureTimesValueArg);
1074
1075    createSubCommand(
1076         ManageAccountSubCommandType.SET_AUTHENTICATION_FAILURE_TIMES,
1077         setAuthFailureTimesParser,
1078         createSubCommandExample(
1079              ManageAccountSubCommandType.SET_AUTHENTICATION_FAILURE_TIMES,
1080              INFO_MANAGE_ACCT_SC_SET_AUTH_FAILURE_TIMES_EXAMPLE.get(
1081                   EXAMPLE_TARGET_USER_DN, olderGeneralizedTime,
1082                   currentGeneralizedTime),
1083              "--authenticationFailureTime", olderGeneralizedTime,
1084              "--authenticationFailureTime", currentGeneralizedTime));
1085
1086
1087    // Define the subcommand to clear the authentication failure times.
1088    createSubCommand(
1089         ManageAccountSubCommandType.CLEAR_AUTHENTICATION_FAILURE_TIMES,
1090         INFO_MANAGE_ACCT_SC_CLEAR_AUTH_FAILURE_TIMES_EXAMPLE.get(
1091              EXAMPLE_TARGET_USER_DN));
1092
1093
1094    // Define the subcommand to get the remaining authentication failure count.
1095    createSubCommand(
1096         ManageAccountSubCommandType.GET_REMAINING_AUTHENTICATION_FAILURE_COUNT,
1097         INFO_MANAGE_ACCT_SC_GET_REMAINING_FAILURE_COUNT_EXAMPLE.get(
1098              EXAMPLE_TARGET_USER_DN));
1099
1100
1101    // Define the subcommand to determine whether the account is idle locked.
1102    createSubCommand(ManageAccountSubCommandType.GET_ACCOUNT_IS_IDLE_LOCKED,
1103         INFO_MANAGE_ACCT_SC_GET_ACCT_IDLE_LOCKED_EXAMPLE.get(
1104              EXAMPLE_TARGET_USER_DN));
1105
1106
1107    // Define the subcommand to get the length of time until the account is
1108    // idle locked.
1109    createSubCommand(ManageAccountSubCommandType.GET_SECONDS_UNTIL_IDLE_LOCKOUT,
1110         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_IDLE_LOCKOUT_EXAMPLE.get(
1111              EXAMPLE_TARGET_USER_DN));
1112
1113
1114    // Define the subcommand to get the idle lockout time for an account.
1115    createSubCommand(ManageAccountSubCommandType.GET_IDLE_LOCKOUT_TIME,
1116         INFO_MANAGE_ACCT_SC_GET_IDLE_LOCKOUT_TIME_EXAMPLE.get(
1117              EXAMPLE_TARGET_USER_DN));
1118
1119
1120    // Define the subcommand to determine whether a user's password has been
1121    // reset.
1122    createSubCommand(ManageAccountSubCommandType.GET_MUST_CHANGE_PASSWORD,
1123         INFO_MANAGE_ACCT_SC_GET_MUST_CHANGE_PW_EXAMPLE.get(
1124              EXAMPLE_TARGET_USER_DN));
1125
1126
1127    // Define the subcommand to specify whether a user's password has been
1128    // reset.
1129    final ArgumentParser setPWIsResetParser =
1130         createSubCommandParser(
1131              ManageAccountSubCommandType.SET_MUST_CHANGE_PASSWORD);
1132
1133    final BooleanValueArgument setPWIsResetValueArg =
1134         new BooleanValueArgument('O', "mustChangePassword", true, null,
1135              INFO_MANAGE_ACCT_SC_SET_MUST_CHANGE_PW_ARG_VALUE.get());
1136    setPWIsResetValueArg.addLongIdentifier("passwordIsReset", true);
1137    setPWIsResetValueArg.addLongIdentifier("operationValue", true);
1138    setPWIsResetValueArg.addLongIdentifier("must-change-password", true);
1139    setPWIsResetValueArg.addLongIdentifier("password-is-reset", true);
1140    setPWIsResetValueArg.addLongIdentifier("operation-value", true);
1141    setPWIsResetParser.addArgument(setPWIsResetValueArg);
1142
1143    createSubCommand(ManageAccountSubCommandType.SET_MUST_CHANGE_PASSWORD,
1144         setPWIsResetParser,
1145         createSubCommandExample(
1146              ManageAccountSubCommandType.SET_MUST_CHANGE_PASSWORD,
1147              INFO_MANAGE_ACCT_SC_SET_MUST_CHANGE_PW_EXAMPLE.get(
1148                   EXAMPLE_TARGET_USER_DN),
1149              "--mustChangePassword", "true"));
1150
1151
1152    // Define the subcommand to clear the password reset state information.
1153    createSubCommand(ManageAccountSubCommandType.CLEAR_MUST_CHANGE_PASSWORD,
1154         INFO_MANAGE_ACCT_SC_CLEAR_MUST_CHANGE_PW_EXAMPLE.get(
1155              EXAMPLE_TARGET_USER_DN));
1156
1157
1158    // Define the subcommand to determine whether the account is reset locked.
1159    createSubCommand(
1160         ManageAccountSubCommandType.GET_ACCOUNT_IS_PASSWORD_RESET_LOCKED,
1161         INFO_MANAGE_ACCT_SC_GET_ACCT_IS_RESET_LOCKED_EXAMPLE.get(
1162              EXAMPLE_TARGET_USER_DN));
1163
1164
1165    // Define the subcommand to get the length of time until the password is
1166    // reset locked.
1167    createSubCommand(
1168         ManageAccountSubCommandType.GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
1169         INFO_MANAGE_ACCT_SC_GET_SECONDS_UNTIL_RESET_LOCKOUT_EXAMPLE.get(
1170              EXAMPLE_TARGET_USER_DN));
1171
1172
1173    // Define the subcommand to get the password reset lockout time.
1174    createSubCommand(
1175         ManageAccountSubCommandType.GET_PASSWORD_RESET_LOCKOUT_TIME,
1176         INFO_MANAGE_ACCT_SC_GET_RESET_LOCKOUT_TIME_EXAMPLE.get(
1177              EXAMPLE_TARGET_USER_DN));
1178
1179
1180    // Define the subcommand to get the last login time.
1181    createSubCommand(ManageAccountSubCommandType.GET_LAST_LOGIN_TIME,
1182         INFO_MANAGE_ACCT_SC_GET_LAST_LOGIN_TIME_EXAMPLE.get(
1183              EXAMPLE_TARGET_USER_DN));
1184
1185
1186    // Define the subcommand to set the last login time.
1187    final ArgumentParser setLastLoginTimeParser =
1188         createSubCommandParser(
1189              ManageAccountSubCommandType.SET_LAST_LOGIN_TIME);
1190
1191    final TimestampArgument setLastLoginTimeValueArg = new TimestampArgument(
1192         'O', "lastLoginTime", false, 1, null,
1193         INFO_MANAGE_ACCT_SC_SET_LAST_LOGIN_TIME_ARG_VALUE.get());
1194    setLastLoginTimeValueArg.addLongIdentifier("operationValue", true);
1195    setLastLoginTimeValueArg.addLongIdentifier("last-login-time", true);
1196    setLastLoginTimeValueArg.addLongIdentifier("operation-value", true);
1197    setLastLoginTimeParser.addArgument(setLastLoginTimeValueArg);
1198
1199    createSubCommand(ManageAccountSubCommandType.SET_LAST_LOGIN_TIME,
1200         setLastLoginTimeParser,
1201         createSubCommandExample(
1202              ManageAccountSubCommandType.SET_LAST_LOGIN_TIME,
1203              INFO_MANAGE_ACCT_SC_SET_LAST_LOGIN_TIME_EXAMPLE.get(
1204                   EXAMPLE_TARGET_USER_DN, currentGeneralizedTime),
1205              "--lastLoginTime", currentGeneralizedTime));
1206
1207
1208    // Define the subcommand to clear the last login time.
1209    createSubCommand(ManageAccountSubCommandType.CLEAR_LAST_LOGIN_TIME,
1210         INFO_MANAGE_ACCT_SC_CLEAR_LAST_LOGIN_TIME_EXAMPLE.get(
1211              EXAMPLE_TARGET_USER_DN));
1212
1213
1214    // Define the subcommand to get the last login IP address.
1215    createSubCommand(ManageAccountSubCommandType.GET_LAST_LOGIN_IP_ADDRESS,
1216         INFO_MANAGE_ACCT_SC_GET_LAST_LOGIN_IP_EXAMPLE.get(
1217              EXAMPLE_TARGET_USER_DN));
1218
1219
1220    // Define the subcommand to set the last login IP address.
1221    final ArgumentParser setLastLoginIPParser =
1222         createSubCommandParser(
1223              ManageAccountSubCommandType.SET_LAST_LOGIN_IP_ADDRESS);
1224
1225    final StringArgument setLastLoginIPValueArg = new StringArgument('O',
1226         "lastLoginIPAddress", true, 1, null,
1227         INFO_MANAGE_ACCT_SC_SET_LAST_LOGIN_IP_ARG_VALUE.get());
1228    setLastLoginIPValueArg.addLongIdentifier("operationValue", true);
1229    setLastLoginIPValueArg.addLongIdentifier("last-login-ip-address", true);
1230    setLastLoginIPValueArg.addLongIdentifier("operation-value", true);
1231    setLastLoginIPValueArg.addValueValidator(
1232         new IPAddressArgumentValueValidator());
1233    setLastLoginIPParser.addArgument(setLastLoginIPValueArg);
1234
1235
1236    createSubCommand(ManageAccountSubCommandType.SET_LAST_LOGIN_IP_ADDRESS,
1237         setLastLoginIPParser,
1238         createSubCommandExample(
1239              ManageAccountSubCommandType.SET_LAST_LOGIN_IP_ADDRESS,
1240              INFO_MANAGE_ACCT_SC_SET_LAST_LOGIN_IP_EXAMPLE.get(
1241                   EXAMPLE_TARGET_USER_DN, "1.2.3.4"),
1242              "--lastLoginIPAddress", "1.2.3.4"));
1243
1244
1245    // Define the subcommand to clear the last login IP address.
1246    createSubCommand(ManageAccountSubCommandType.CLEAR_LAST_LOGIN_IP_ADDRESS,
1247         INFO_MANAGE_ACCT_SC_CLEAR_LAST_LOGIN_IP_EXAMPLE.get(
1248              EXAMPLE_TARGET_USER_DN));
1249
1250
1251    // Define the subcommand to get the grace login use times.
1252    createSubCommand(ManageAccountSubCommandType.GET_GRACE_LOGIN_USE_TIMES,
1253         INFO_MANAGE_ACCT_SC_GET_GRACE_LOGIN_TIMES_EXAMPLE.get(
1254              EXAMPLE_TARGET_USER_DN));
1255
1256
1257    // Define the subcommand to add values to the set of grace login use times.
1258    final ArgumentParser addGraceLoginTimeParser =
1259         createSubCommandParser(
1260              ManageAccountSubCommandType.ADD_GRACE_LOGIN_USE_TIME);
1261
1262    final TimestampArgument addGraceLoginTimeValueArg =
1263         new TimestampArgument('O', "graceLoginUseTime", false, 0, null,
1264              INFO_MANAGE_ACCT_SC_ADD_GRACE_LOGIN_TIME_ARG_VALUE.get());
1265    addGraceLoginTimeValueArg.addLongIdentifier("operationValue", true);
1266    addGraceLoginTimeValueArg.addLongIdentifier("grace-login-use-time", true);
1267    addGraceLoginTimeValueArg.addLongIdentifier("operation-value", true);
1268    addGraceLoginTimeParser.addArgument(addGraceLoginTimeValueArg);
1269
1270    createSubCommand(ManageAccountSubCommandType.ADD_GRACE_LOGIN_USE_TIME,
1271         addGraceLoginTimeParser,
1272         createSubCommandExample(
1273              ManageAccountSubCommandType.ADD_GRACE_LOGIN_USE_TIME,
1274              INFO_MANAGE_ACCT_SC_ADD_GRACE_LOGIN_TIME_EXAMPLE.get(
1275                   EXAMPLE_TARGET_USER_DN)));
1276
1277
1278    // Define the subcommand to replace the set of grace login use times.
1279    final ArgumentParser setGraceLoginTimesParser =
1280         createSubCommandParser(
1281              ManageAccountSubCommandType.SET_GRACE_LOGIN_USE_TIMES);
1282
1283    final TimestampArgument setGraceLoginTimesValueArg =
1284         new TimestampArgument('O', "graceLoginUseTime", false, 0, null,
1285              INFO_MANAGE_ACCT_SC_SET_GRACE_LOGIN_TIMES_ARG_VALUE.get());
1286    setGraceLoginTimesValueArg.addLongIdentifier("operationValue", true);
1287    setGraceLoginTimesValueArg.addLongIdentifier("grace-login-use-time", true);
1288    setGraceLoginTimesValueArg.addLongIdentifier("operation-value", true);
1289    setGraceLoginTimesParser.addArgument(setGraceLoginTimesValueArg);
1290
1291    createSubCommand(ManageAccountSubCommandType.SET_GRACE_LOGIN_USE_TIMES,
1292         setGraceLoginTimesParser,
1293         createSubCommandExample(
1294              ManageAccountSubCommandType.SET_GRACE_LOGIN_USE_TIMES,
1295              INFO_MANAGE_ACCT_SC_SET_GRACE_LOGIN_TIMES_EXAMPLE.get(
1296                   EXAMPLE_TARGET_USER_DN, olderGeneralizedTime,
1297                   currentGeneralizedTime),
1298              "--graceLoginUseTime", olderGeneralizedTime,
1299              "--graceLoginUseTime", currentGeneralizedTime));
1300
1301
1302    // Define the subcommand to clear the grace login use times.
1303    createSubCommand(ManageAccountSubCommandType.CLEAR_GRACE_LOGIN_USE_TIMES,
1304         INFO_MANAGE_ACCT_SC_CLEAR_GRACE_LOGIN_TIMES_EXAMPLE.get(
1305              EXAMPLE_TARGET_USER_DN));
1306
1307
1308    // Define the subcommand to get the remaining grace login count.
1309    createSubCommand(
1310         ManageAccountSubCommandType.GET_REMAINING_GRACE_LOGIN_COUNT,
1311         INFO_MANAGE_ACCT_SC_GET_REMAINING_GRACE_LOGIN_COUNT_EXAMPLE.get(
1312              EXAMPLE_TARGET_USER_DN));
1313
1314
1315    // Define the subcommand to get the password changed by required time value.
1316    createSubCommand(
1317         ManageAccountSubCommandType.GET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1318         INFO_MANAGE_ACCT_SC_GET_PW_CHANGED_BY_REQ_TIME_EXAMPLE.get(
1319              EXAMPLE_TARGET_USER_DN));
1320
1321
1322    // Define the subcommand to set the password changed by required time value.
1323    final ArgumentParser setPWChangedByReqTimeParser =
1324         createSubCommandParser(ManageAccountSubCommandType.
1325              SET_PASSWORD_CHANGED_BY_REQUIRED_TIME);
1326
1327    final TimestampArgument setPWChangedByReqTimeValueArg =
1328         new TimestampArgument('O', "passwordChangedByRequiredTime", false, 1,
1329              null,
1330              INFO_MANAGE_ACCT_SC_SET_PW_CHANGED_BY_REQ_TIME_ARG_VALUE.get());
1331    setPWChangedByReqTimeValueArg.addLongIdentifier("operationValue", true);
1332    setPWChangedByReqTimeValueArg.addLongIdentifier(
1333         "password-changed-by-required-time", true);
1334    setPWChangedByReqTimeValueArg.addLongIdentifier("operation-value", true);
1335    setPWChangedByReqTimeParser.addArgument(
1336         setPWChangedByReqTimeValueArg);
1337
1338    createSubCommand(
1339         ManageAccountSubCommandType.SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1340         setPWChangedByReqTimeParser,
1341         createSubCommandExample(
1342              ManageAccountSubCommandType.SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1343              INFO_MANAGE_ACCT_SC_SET_PW_CHANGED_BY_REQ_TIME_EXAMPLE.get(
1344                   EXAMPLE_TARGET_USER_DN)));
1345
1346
1347    // Define the subcommand to clear the password changed by required time
1348    // value.
1349    createSubCommand(
1350         ManageAccountSubCommandType.CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1351         INFO_MANAGE_ACCT_SC_CLEAR_PW_CHANGED_BY_REQ_TIME_EXAMPLE.get(
1352              EXAMPLE_TARGET_USER_DN));
1353
1354
1355    // Define the subcommand to get the length of time until the required change
1356    // time.
1357    createSubCommand(
1358         ManageAccountSubCommandType.
1359              GET_SECONDS_UNTIL_REQUIRED_PASSWORD_CHANGE_TIME,
1360         INFO_MANAGE_ACCT_SC_GET_SECS_UNTIL_REQ_CHANGE_TIME_EXAMPLE.get(
1361              EXAMPLE_TARGET_USER_DN));
1362
1363
1364    // Define the subcommand to get the password history count.
1365    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_HISTORY_COUNT,
1366         INFO_MANAGE_ACCT_SC_GET_PW_HISTORY_COUNT_EXAMPLE.get(
1367              EXAMPLE_TARGET_USER_DN));
1368
1369
1370    // Define the subcommand to clear a user's password history.
1371    createSubCommand(ManageAccountSubCommandType.CLEAR_PASSWORD_HISTORY,
1372         INFO_MANAGE_ACCT_SC_CLEAR_PW_HISTORY_EXAMPLE.get(
1373              EXAMPLE_TARGET_USER_DN));
1374
1375
1376    // Define the subcommand to determine whether a user has a retired password.
1377    createSubCommand(ManageAccountSubCommandType.GET_HAS_RETIRED_PASSWORD,
1378         INFO_MANAGE_ACCT_SC_GET_HAS_RETIRED_PW_EXAMPLE.get(
1379              EXAMPLE_TARGET_USER_DN));
1380
1381
1382    // Define the subcommand to retrieve the time that a user's former password
1383    // was retired.
1384    createSubCommand(ManageAccountSubCommandType.GET_PASSWORD_RETIRED_TIME,
1385         INFO_MANAGE_ACCT_SC_GET_PW_RETIRED_TIME_EXAMPLE.get(
1386              EXAMPLE_TARGET_USER_DN));
1387
1388
1389    // Define the subcommand to retrieve the retired password expiration time.
1390    createSubCommand(
1391         ManageAccountSubCommandType.GET_RETIRED_PASSWORD_EXPIRATION_TIME,
1392         INFO_MANAGE_ACCT_SC_GET_RETIRED_PW_EXP_TIME_EXAMPLE.get(
1393              EXAMPLE_TARGET_USER_DN));
1394
1395
1396    // Define the subcommand to purge a retired password.
1397    createSubCommand(ManageAccountSubCommandType.CLEAR_RETIRED_PASSWORD,
1398         INFO_MANAGE_ACCT_SC_PURGE_RETIRED_PW_EXAMPLE.get(
1399              EXAMPLE_TARGET_USER_DN));
1400
1401
1402    // Define the subcommand to get the available SASL mechanisms for a user.
1403    createSubCommand(ManageAccountSubCommandType.GET_AVAILABLE_SASL_MECHANISMS,
1404         INFO_MANAGE_ACCT_SC_GET_AVAILABLE_SASL_MECHS_EXAMPLE.get(
1405              EXAMPLE_TARGET_USER_DN));
1406
1407
1408    // Define the subcommand to get the available OTP delivery mechanisms for a
1409    // user.
1410    createSubCommand(
1411         ManageAccountSubCommandType.GET_AVAILABLE_OTP_DELIVERY_MECHANISMS,
1412         INFO_MANAGE_ACCT_SC_GET_AVAILABLE_OTP_MECHS_EXAMPLE.get(
1413              EXAMPLE_TARGET_USER_DN));
1414
1415
1416    // Define the subcommand to determine whether a user has at least one TOTP
1417    // shared secret.
1418    createSubCommand(ManageAccountSubCommandType.GET_HAS_TOTP_SHARED_SECRET,
1419         INFO_MANAGE_ACCT_SC_GET_HAS_TOTP_SHARED_SECRET_EXAMPLE.get(
1420              EXAMPLE_TARGET_USER_DN));
1421
1422
1423    // Define the subcommand to add a value to the set of TOTP shared secrets
1424    // for a user.
1425    final ArgumentParser addTOTPSharedSecretParser =
1426         createSubCommandParser(
1427              ManageAccountSubCommandType.ADD_TOTP_SHARED_SECRET);
1428
1429    final StringArgument addTOTPSharedSecretValueArg =
1430         new StringArgument('O', "totpSharedSecret", true, 0, null,
1431              INFO_MANAGE_ACCT_SC_ADD_YUBIKEY_ID_ARG_VALUE.get());
1432    addTOTPSharedSecretValueArg.addLongIdentifier("operationValue", true);
1433    addTOTPSharedSecretValueArg.addLongIdentifier("totp-shared-secret", true);
1434    addTOTPSharedSecretValueArg.addLongIdentifier("operation-value", true);
1435    addTOTPSharedSecretValueArg.setSensitive(true);
1436    addTOTPSharedSecretParser.addArgument(
1437         addTOTPSharedSecretValueArg);
1438
1439    createSubCommand(ManageAccountSubCommandType.ADD_TOTP_SHARED_SECRET,
1440         addTOTPSharedSecretParser,
1441         createSubCommandExample(
1442              ManageAccountSubCommandType.ADD_TOTP_SHARED_SECRET,
1443              INFO_MANAGE_ACCT_SC_ADD_TOTP_SHARED_SECRET_EXAMPLE.get(
1444                   "abcdefghijklmnop", EXAMPLE_TARGET_USER_DN),
1445              "--totpSharedSecret", "abcdefghijklmnop"));
1446
1447
1448    // Define the subcommand to remove a value from the set of TOTP shared
1449    // secrets for a user.
1450    final ArgumentParser removeTOTPSharedSecretParser =
1451         createSubCommandParser(
1452              ManageAccountSubCommandType.REMOVE_TOTP_SHARED_SECRET);
1453
1454    final StringArgument removeTOTPSharedSecretValueArg =
1455         new StringArgument('O', "totpSharedSecret", true, 0, null,
1456              INFO_MANAGE_ACCT_SC_REMOVE_YUBIKEY_ID_ARG_VALUE.get());
1457    removeTOTPSharedSecretValueArg.addLongIdentifier("operationValue", true);
1458    removeTOTPSharedSecretValueArg.addLongIdentifier("totp-shared-secret",
1459         true);
1460    removeTOTPSharedSecretValueArg.addLongIdentifier("operation-value", true);
1461    removeTOTPSharedSecretValueArg.setSensitive(true);
1462    removeTOTPSharedSecretParser.addArgument(
1463         removeTOTPSharedSecretValueArg);
1464
1465    createSubCommand(ManageAccountSubCommandType.REMOVE_TOTP_SHARED_SECRET,
1466         removeTOTPSharedSecretParser,
1467         createSubCommandExample(
1468              ManageAccountSubCommandType.REMOVE_TOTP_SHARED_SECRET,
1469              INFO_MANAGE_ACCT_SC_REMOVE_TOTP_SHARED_SECRET_EXAMPLE.get(
1470                   "abcdefghijklmnop", EXAMPLE_TARGET_USER_DN),
1471              "--totpSharedSecret", "abcdefghijklmnop"));
1472
1473
1474    // Define the subcommand to replace set of TOTP shared secrets for a user.
1475    final ArgumentParser setTOTPSharedSecretsParser =
1476         createSubCommandParser(
1477              ManageAccountSubCommandType.SET_TOTP_SHARED_SECRETS);
1478
1479    final StringArgument setTOTPSharedSecretsValueArg =
1480         new StringArgument('O', "totpSharedSecret", true, 0, null,
1481              INFO_MANAGE_ACCT_SC_SET_TOTP_SHARED_SECRETS_ARG_VALUE.get());
1482    setTOTPSharedSecretsValueArg.addLongIdentifier("operationValue", true);
1483    setTOTPSharedSecretsValueArg.addLongIdentifier("totp-shared-secret", true);
1484    setTOTPSharedSecretsValueArg.addLongIdentifier("operation-value", true);
1485    setTOTPSharedSecretsValueArg.setSensitive(true);
1486    setTOTPSharedSecretsParser.addArgument(
1487         setTOTPSharedSecretsValueArg);
1488
1489    createSubCommand(ManageAccountSubCommandType.SET_TOTP_SHARED_SECRETS,
1490         setTOTPSharedSecretsParser,
1491         createSubCommandExample(
1492              ManageAccountSubCommandType.SET_TOTP_SHARED_SECRETS,
1493              INFO_MANAGE_ACCT_SC_SET_TOTP_SHARED_SECRETS_EXAMPLE.get(
1494                   EXAMPLE_TARGET_USER_DN, "abcdefghijklmnop"),
1495              "--totpSharedSecret", "abcdefghijklmnop"));
1496
1497
1498    // Define the subcommand to clear the set of TOTP shared secrets for a user.
1499    createSubCommand(
1500         ManageAccountSubCommandType.CLEAR_TOTP_SHARED_SECRETS,
1501         INFO_MANAGE_ACCT_SC_CLEAR_TOTP_SHARED_SECRETS_EXAMPLE.get(
1502              EXAMPLE_TARGET_USER_DN));
1503
1504
1505    // Define the subcommand to determine whether a user has at least one
1506    // registered YubiKey OTP device public ID.
1507    createSubCommand(
1508         ManageAccountSubCommandType.GET_HAS_REGISTERED_YUBIKEY_PUBLIC_ID,
1509         INFO_MANAGE_ACCT_SC_GET_HAS_YUBIKEY_ID_EXAMPLE.get(
1510              EXAMPLE_TARGET_USER_DN));
1511
1512
1513    // Define the subcommand to get the set of registered YubiKey OTP device
1514    // public IDs for a user.
1515    createSubCommand(
1516         ManageAccountSubCommandType.GET_REGISTERED_YUBIKEY_PUBLIC_IDS,
1517         INFO_MANAGE_ACCT_SC_GET_YUBIKEY_IDS_EXAMPLE.get(
1518              EXAMPLE_TARGET_USER_DN));
1519
1520
1521    // Define the subcommand to add a value to the set of registered YubiKey OTP
1522    // device public IDs for a user.
1523    final ArgumentParser addRegisteredYubiKeyPublicIDParser =
1524         createSubCommandParser(
1525              ManageAccountSubCommandType.ADD_REGISTERED_YUBIKEY_PUBLIC_ID);
1526
1527    final StringArgument addRegisteredYubiKeyPublicIDValueArg =
1528         new StringArgument('O', "publicID", true, 0, null,
1529              INFO_MANAGE_ACCT_SC_ADD_YUBIKEY_ID_ARG_VALUE.get());
1530    addRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operationValue",
1531         true);
1532    addRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("public-id", true);
1533    addRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operation-value",
1534         true);
1535    addRegisteredYubiKeyPublicIDParser.addArgument(
1536         addRegisteredYubiKeyPublicIDValueArg);
1537
1538    createSubCommand(
1539         ManageAccountSubCommandType.ADD_REGISTERED_YUBIKEY_PUBLIC_ID,
1540         addRegisteredYubiKeyPublicIDParser,
1541         createSubCommandExample(
1542              ManageAccountSubCommandType.ADD_REGISTERED_YUBIKEY_PUBLIC_ID,
1543              INFO_MANAGE_ACCT_SC_ADD_YUBIKEY_ID_EXAMPLE.get(
1544                   "abcdefghijkl", EXAMPLE_TARGET_USER_DN),
1545              "--publicID", "abcdefghijkl"));
1546
1547
1548    // Define the subcommand to remove a value from the set of registered
1549    // YubiKey OTP device public IDs for a user.
1550    final ArgumentParser removeRegisteredYubiKeyPublicIDParser =
1551         createSubCommandParser(
1552              ManageAccountSubCommandType.REMOVE_REGISTERED_YUBIKEY_PUBLIC_ID);
1553
1554    final StringArgument removeRegisteredYubiKeyPublicIDValueArg =
1555         new StringArgument('O', "publicID", true, 0, null,
1556              INFO_MANAGE_ACCT_SC_REMOVE_YUBIKEY_ID_ARG_VALUE.get());
1557    removeRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operationValue",
1558         true);
1559    removeRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("public-id",
1560         true);
1561    removeRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operation-value",
1562         true);
1563    removeRegisteredYubiKeyPublicIDParser.addArgument(
1564         removeRegisteredYubiKeyPublicIDValueArg);
1565
1566    createSubCommand(
1567         ManageAccountSubCommandType.REMOVE_REGISTERED_YUBIKEY_PUBLIC_ID,
1568         removeRegisteredYubiKeyPublicIDParser,
1569         createSubCommandExample(
1570              ManageAccountSubCommandType.REMOVE_REGISTERED_YUBIKEY_PUBLIC_ID,
1571              INFO_MANAGE_ACCT_SC_REMOVE_YUBIKEY_ID_EXAMPLE.get(
1572                   "abcdefghijkl", EXAMPLE_TARGET_USER_DN),
1573              "--publicID", "abcdefghijkl"));
1574
1575
1576    // Define the subcommand to replace set of registered YubiKey OTP device
1577    // public IDs for a user.
1578    final ArgumentParser setRegisteredYubiKeyPublicIDParser =
1579         createSubCommandParser(
1580              ManageAccountSubCommandType.SET_REGISTERED_YUBIKEY_PUBLIC_IDS);
1581
1582    final StringArgument setRegisteredYubiKeyPublicIDValueArg =
1583         new StringArgument('O', "publicID", true, 0, null,
1584              INFO_MANAGE_ACCT_SC_SET_YUBIKEY_IDS_ARG_VALUE.get());
1585    setRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operationValue",
1586         true);
1587    setRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("public-id", true);
1588    setRegisteredYubiKeyPublicIDValueArg.addLongIdentifier("operation-value",
1589         true);
1590    setRegisteredYubiKeyPublicIDParser.addArgument(
1591         setRegisteredYubiKeyPublicIDValueArg);
1592
1593    createSubCommand(
1594         ManageAccountSubCommandType.SET_REGISTERED_YUBIKEY_PUBLIC_IDS,
1595         setRegisteredYubiKeyPublicIDParser,
1596         createSubCommandExample(
1597              ManageAccountSubCommandType.SET_REGISTERED_YUBIKEY_PUBLIC_IDS,
1598              INFO_MANAGE_ACCT_SC_SET_YUBIKEY_IDS_EXAMPLE.get(
1599                   EXAMPLE_TARGET_USER_DN, "abcdefghijkl"),
1600              "--publicID", "abcdefghijkl"));
1601
1602
1603    // Define the subcommand to clear the set of registered YubiKey OTP device
1604    // public IDs for a user.
1605    createSubCommand(
1606         ManageAccountSubCommandType.CLEAR_REGISTERED_YUBIKEY_PUBLIC_IDS,
1607         INFO_MANAGE_ACCT_SC_CLEAR_YUBIKEY_IDS_EXAMPLE.get(
1608              EXAMPLE_TARGET_USER_DN));
1609
1610
1611    // Define the subcommand to determine whether a user has at least one static
1612    // password.
1613    createSubCommand(ManageAccountSubCommandType.GET_HAS_STATIC_PASSWORD,
1614         INFO_MANAGE_ACCT_SC_GET_HAS_STATIC_PW_EXAMPLE.get(
1615              EXAMPLE_TARGET_USER_DN));
1616  }
1617
1618
1619
1620  /**
1621   * Creates an argument parser for the provided subcommand type.  It will not
1622   * have any arguments associated with it.
1623   *
1624   * @param  type  The subcommand type for which to create the argument parser.
1625   *
1626   * @return  The created argument parser.
1627   *
1628   * @throws  ArgumentException  If a problem is encountered while creating the
1629   *                             argument parser.
1630   */
1631  private static ArgumentParser createSubCommandParser(
1632                                     final ManageAccountSubCommandType type)
1633          throws ArgumentException
1634  {
1635    return new ArgumentParser(type.getPrimaryName(), type.getDescription());
1636  }
1637
1638
1639
1640  /**
1641   * Generates an example usage map for a specified subcommand.
1642   *
1643   * @param  t            The subcommand type.
1644   * @param  description  The description to use for the example.
1645   * @param  args         The set of arguments to include in the example,
1646   *                      excluding the subcommand name and the arguments used
1647   *                      to connect and authenticate to the server.  This may
1648   *                      be empty if no additional arguments are needed.
1649   *
1650   * @return The generated example usage map.
1651   */
1652  private static LinkedHashMap<String[],String> createSubCommandExample(
1653                      final ManageAccountSubCommandType t,
1654                      final String description, final String... args)
1655  {
1656    final LinkedHashMap<String[], String> examples =
1657         new LinkedHashMap<>(StaticUtils.computeMapCapacity(1));
1658    createSubCommandExample(examples, t, description, args);
1659    return examples;
1660  }
1661
1662
1663
1664  /**
1665   * Adds an example for a specified subcommand to the given map.
1666   *
1667   * @param  examples     The map to which the example should be added.
1668   * @param  t            The subcommand type.
1669   * @param  description  The description to use for the example.
1670   * @param  args         The set of arguments to include in the example,
1671   *                      excluding the subcommand name and the arguments used
1672   *                      to connect and authenticate to the server.  This may
1673   *                      be empty if no additional arguments are needed.
1674   */
1675  private static void createSubCommandExample(
1676       final LinkedHashMap<String[], String> examples,
1677       final ManageAccountSubCommandType t, final String description,
1678       final String... args)
1679  {
1680    final ArrayList<String> argList = new ArrayList<>(10 + args.length);
1681    argList.add(t.getPrimaryName());
1682    argList.add("--hostname");
1683    argList.add("server.example.com");
1684    argList.add("--port");
1685    argList.add("389");
1686    argList.add("--bindDN");
1687    argList.add("uid=admin,dc=example,dc=com");
1688    argList.add("--promptForBindPassword");
1689    argList.add("--targetDN");
1690    argList.add("uid=jdoe,ou=People,dc=example,dc=com");
1691
1692    if (args.length > 0)
1693    {
1694      argList.addAll(Arrays.asList(args));
1695    }
1696
1697    final String[] argArray = new String[argList.size()];
1698    argList.toArray(argArray);
1699
1700    examples.put(argArray, description);
1701  }
1702
1703
1704
1705  /**
1706   * Creates a subcommand with the provided information.
1707   *
1708   * @param  subcommandType       The subcommand type.
1709   * @param  exampleDescription   The description to use for the
1710   *                              automatically-generated example.
1711   *
1712   * @throws  ArgumentException  If a problem is encountered while creating the
1713   *                             subcommand.
1714   */
1715  private void createSubCommand(
1716                    final ManageAccountSubCommandType subcommandType,
1717                    final String exampleDescription)
1718          throws ArgumentException
1719  {
1720    final ArgumentParser subcommandParser =
1721         createSubCommandParser(subcommandType);
1722
1723    final LinkedHashMap<String[],String> examples =
1724         createSubCommandExample(subcommandType, exampleDescription);
1725
1726    createSubCommand(subcommandType, subcommandParser, examples);
1727  }
1728
1729
1730
1731  /**
1732   * Creates a subcommand with the provided information.
1733   *
1734   * @param  subcommandType    The subcommand type.
1735   * @param  subcommandParser  The argument parser for the subcommand-specific
1736   *                           arguments.
1737   * @param  examples          The example usages for the subcommand.
1738   *
1739   * @throws  ArgumentException  If a problem is encountered while creating the
1740   *                             subcommand.
1741   */
1742  private void createSubCommand(
1743                    final ManageAccountSubCommandType subcommandType,
1744                    final ArgumentParser subcommandParser,
1745                    final LinkedHashMap<String[],String> examples)
1746          throws ArgumentException
1747  {
1748    final SubCommand subCommand = new SubCommand(
1749         subcommandType.getPrimaryName(), subcommandType.getDescription(),
1750         subcommandParser, examples);
1751
1752    for (final String alternateName : subcommandType.getAlternateNames())
1753    {
1754      subCommand.addName(alternateName, true);
1755    }
1756
1757    parser.addSubCommand(subCommand);
1758  }
1759
1760
1761
1762  /**
1763   * {@inheritDoc}
1764   */
1765  @Override()
1766  public LDAPConnectionOptions getConnectionOptions()
1767  {
1768    return connectionOptions;
1769  }
1770
1771
1772
1773  /**
1774   * {@inheritDoc}
1775   */
1776  @Override()
1777  public ResultCode doToolProcessing()
1778  {
1779    // If we should just generate a sample rate data file, then do that now.
1780    final FileArgument generateSampleRateFile =
1781         parser.getFileArgument(ARG_GENERATE_SAMPLE_RATE_FILE);
1782    if (generateSampleRateFile.isPresent())
1783    {
1784      try
1785      {
1786        RateAdjustor.writeSampleVariableRateFile(
1787             generateSampleRateFile.getValue());
1788        return ResultCode.SUCCESS;
1789      }
1790      catch (final Exception e)
1791      {
1792        Debug.debugException(e);
1793        wrapErr(0, WRAP_COLUMN,
1794             ERR_MANAGE_ACCT_CANNOT_GENERATE_SAMPLE_RATE_FILE.get(
1795                  generateSampleRateFile.getValue().getAbsolutePath(),
1796                  StaticUtils.getExceptionMessage(e)));
1797        return ResultCode.LOCAL_ERROR;
1798      }
1799    }
1800
1801
1802    // If we need to create a fixed-rate barrier and/or use a variable rate
1803    // definition, then set that up.
1804    final IntegerArgument ratePerSecond =
1805         parser.getIntegerArgument(ARG_RATE_PER_SECOND);
1806    final FileArgument variableRateData =
1807         parser.getFileArgument(ARG_VARIABLE_RATE_DATA);
1808    if (ratePerSecond.isPresent() || variableRateData.isPresent())
1809    {
1810      if (ratePerSecond.isPresent())
1811      {
1812        rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue());
1813      }
1814      else
1815      {
1816        rateLimiter = new FixedRateBarrier(1000L, Integer.MAX_VALUE);
1817      }
1818
1819      if (variableRateData.isPresent())
1820      {
1821        try
1822        {
1823          rateAdjustor = RateAdjustor.newInstance(rateLimiter,
1824               ratePerSecond.getValue(), variableRateData.getValue());
1825        }
1826        catch (final Exception e)
1827        {
1828          Debug.debugException(e);
1829          wrapErr(0, WRAP_COLUMN,
1830               ERR_MANAGE_ACCT_CANNOT_CREATE_RATE_ADJUSTOR.get(
1831                    variableRateData.getValue().getAbsolutePath(),
1832                    StaticUtils.getExceptionMessage(e)));
1833          return ResultCode.PARAM_ERROR;
1834        }
1835      }
1836    }
1837
1838
1839    // Create the connection pool to use for all processing.
1840    final LDAPConnectionPool pool;
1841    final int numSearchThreads =
1842         parser.getIntegerArgument(ARG_NUM_SEARCH_THREADS).getValue();
1843    try
1844    {
1845      final int numOperationThreads =
1846           parser.getIntegerArgument(ARG_NUM_THREADS).getValue();
1847      pool = getConnectionPool(numOperationThreads,
1848           (numOperationThreads + numSearchThreads));
1849
1850      // Explicitly disable automatic retry, since it probably won't work
1851      // reliably for extended operations anyway.  We'll handle retry manually.
1852      pool.setRetryFailedOperationsDueToInvalidConnections(false);
1853
1854      // Set a maximum connection age of 30 minutes.
1855      pool.setMaxConnectionAgeMillis(1_800_000L);
1856    }
1857    catch (final LDAPException le)
1858    {
1859      Debug.debugException(le);
1860
1861      wrapErr(0, WRAP_COLUMN,
1862           ERR_MANAGE_ACCT_CANNOT_CREATE_CONNECTION_POOL.get(getToolName(),
1863                le.getMessage()));
1864      return le.getResultCode();
1865    }
1866
1867
1868    try
1869    {
1870      // Create the output writer.  This should always succeed.
1871      outputWriter = new LDIFWriter(getOut());
1872
1873
1874
1875      // Create the reject writer if appropriate.
1876      final FileArgument rejectFile = parser.getFileArgument(ARG_REJECT_FILE);
1877      if (rejectFile.isPresent())
1878      {
1879        final BooleanArgument appendToRejectFile =
1880             parser.getBooleanArgument(ARG_APPEND_TO_REJECT_FILE);
1881
1882        try
1883        {
1884          rejectWriter = new LDIFWriter(new FileOutputStream(
1885               rejectFile.getValue(), appendToRejectFile.isPresent()));
1886        }
1887        catch (final Exception e)
1888        {
1889          Debug.debugException(e);
1890          wrapErr(0, WRAP_COLUMN,
1891               ERR_MANAGE_ACCT_CANNOT_CREATE_REJECT_WRITER.get(
1892                    rejectFile.getValue().getAbsolutePath(),
1893                    StaticUtils.getExceptionMessage(e)));
1894          return ResultCode.LOCAL_ERROR;
1895        }
1896      }
1897
1898
1899      // Create the processor that will be used to actually perform the
1900      // manage-account operation processing for each entry.
1901      final ManageAccountProcessor processor;
1902      try
1903      {
1904        processor = new ManageAccountProcessor(this, pool, rateLimiter,
1905             outputWriter, rejectWriter);
1906      }
1907      catch (final LDAPException le)
1908      {
1909        Debug.debugException(le);
1910        wrapErr(0, WRAP_COLUMN,
1911             ERR_MANAGE_ACCT_CANNOT_CREATE_PROCESSOR.get(
1912                  StaticUtils.getExceptionMessage(le)));
1913        return le.getResultCode();
1914      }
1915
1916
1917      // If we should use a rate adjustor, then start it now.
1918      if (rateAdjustor != null)
1919      {
1920        rateAdjustor.start();
1921      }
1922
1923
1924      // If any targetDN values were provided, then process them now.
1925      final DNArgument targetDN = parser.getDNArgument(ARG_TARGET_DN);
1926      if (targetDN.isPresent())
1927      {
1928        for (final DN dn : targetDN.getValues())
1929        {
1930          if (cancelRequested())
1931          {
1932            return ResultCode.USER_CANCELED;
1933          }
1934
1935          processor.process(dn.toString());
1936        }
1937      }
1938
1939
1940      // If any DN input files were specified, then process them now.
1941      final FileArgument dnInputFile =
1942           parser.getFileArgument(ARG_DN_INPUT_FILE);
1943      if (dnInputFile.isPresent())
1944      {
1945        for (final File f : dnInputFile.getValues())
1946        {
1947          DNFileReader reader = null;
1948          try
1949          {
1950            reader = new DNFileReader(f);
1951            while (true)
1952            {
1953              if (cancelRequested())
1954              {
1955                return ResultCode.USER_CANCELED;
1956              }
1957
1958              final DN dn;
1959              try
1960              {
1961                dn = reader.readDN();
1962              }
1963              catch (final LDAPException le)
1964              {
1965                Debug.debugException(le);
1966                processor.handleMessage(le.getMessage(), true);
1967                continue;
1968              }
1969
1970              if (dn == null)
1971              {
1972                break;
1973              }
1974
1975              processor.process(dn.toString());
1976            }
1977          }
1978          catch (final Exception e)
1979          {
1980            Debug.debugException(e);
1981            processor.handleMessage(
1982                 ERR_MANAGE_ACCT_ERROR_READING_DN_FILE.get(
1983                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
1984                 true);
1985          }
1986          finally
1987          {
1988            if (reader != null)
1989            {
1990              try
1991              {
1992                reader.close();
1993              }
1994              catch (final Exception e2)
1995              {
1996                Debug.debugException(e2);
1997              }
1998            }
1999          }
2000        }
2001      }
2002
2003
2004      // If any target filters were specified, then process them now.
2005      final FilterArgument targetFilter =
2006           parser.getFilterArgument(ARG_TARGET_FILTER);
2007      if (targetFilter.isPresent())
2008      {
2009        searchProcessor =
2010             new ManageAccountSearchProcessor(this, processor, pool);
2011        for (final Filter f : targetFilter.getValues())
2012        {
2013          searchProcessor.processFilter(f);
2014        }
2015      }
2016
2017
2018      // If any filter input files were specified, then process them now.
2019      final FileArgument filterInputFile =
2020           parser.getFileArgument(ARG_FILTER_INPUT_FILE);
2021      if (filterInputFile.isPresent())
2022      {
2023        if (searchProcessor == null)
2024        {
2025          searchProcessor =
2026               new ManageAccountSearchProcessor(this, processor, pool);
2027        }
2028
2029        for (final File f : filterInputFile.getValues())
2030        {
2031          FilterFileReader reader = null;
2032          try
2033          {
2034            reader = new FilterFileReader(f);
2035            while (true)
2036            {
2037              if (cancelRequested())
2038              {
2039                return ResultCode.USER_CANCELED;
2040              }
2041
2042              final Filter filter;
2043              try
2044              {
2045                filter = reader.readFilter();
2046              }
2047              catch (final LDAPException le)
2048              {
2049                Debug.debugException(le);
2050                processor.handleMessage(le.getMessage(), true);
2051                continue;
2052              }
2053
2054              if (filter == null)
2055              {
2056                break;
2057              }
2058
2059              searchProcessor.processFilter(filter);
2060            }
2061          }
2062          catch (final Exception e)
2063          {
2064            Debug.debugException(e);
2065            processor.handleMessage(
2066                 ERR_MANAGE_ACCT_ERROR_READING_FILTER_FILE.get(
2067                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
2068                 true);
2069          }
2070          finally
2071          {
2072            if (reader != null)
2073            {
2074              try
2075              {
2076                reader.close();
2077              }
2078              catch (final Exception e2)
2079              {
2080                Debug.debugException(e2);
2081              }
2082            }
2083          }
2084        }
2085      }
2086
2087
2088      // If any target user IDs were specified, then process them now.
2089      final StringArgument targetUserID =
2090           parser.getStringArgument(ARG_TARGET_USER_ID);
2091      if (targetUserID.isPresent())
2092      {
2093        if (searchProcessor == null)
2094        {
2095          searchProcessor =
2096               new ManageAccountSearchProcessor(this, processor, pool);
2097        }
2098
2099        for (final String userID : targetUserID.getValues())
2100        {
2101          searchProcessor.processUserID(userID);
2102        }
2103      }
2104
2105
2106      // If any user ID input files were specified, then process them now.
2107      final FileArgument userIDInputFile =
2108           parser.getFileArgument(ARG_USER_ID_INPUT_FILE);
2109      if (userIDInputFile.isPresent())
2110      {
2111        if (searchProcessor == null)
2112        {
2113          searchProcessor =
2114               new ManageAccountSearchProcessor(this, processor, pool);
2115        }
2116
2117        for (final File f : userIDInputFile.getValues())
2118        {
2119          BufferedReader reader = null;
2120          try
2121          {
2122            reader = new BufferedReader(new FileReader(f));
2123            while (true)
2124            {
2125              if (cancelRequested())
2126              {
2127                return ResultCode.USER_CANCELED;
2128              }
2129
2130              final String line = reader.readLine();
2131              if (line == null)
2132              {
2133                break;
2134              }
2135
2136              if ((line.length() == 0) || line.startsWith("#"))
2137              {
2138                continue;
2139              }
2140
2141              searchProcessor.processUserID(line.trim());
2142            }
2143          }
2144          catch (final Exception e)
2145          {
2146            Debug.debugException(e);
2147            processor.handleMessage(
2148                 ERR_MANAGE_ACCT_ERROR_READING_USER_ID_FILE.get(
2149                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
2150                 true);
2151          }
2152          finally
2153          {
2154            if (reader != null)
2155            {
2156              try
2157              {
2158                reader.close();
2159              }
2160              catch (final Exception e2)
2161              {
2162                Debug.debugException(e2);
2163              }
2164            }
2165          }
2166        }
2167      }
2168
2169
2170      allFiltersProvided.set(true);
2171      if (searchProcessor != null)
2172      {
2173        searchProcessor.waitForCompletion();
2174      }
2175
2176      allDNsProvided.set(true);
2177      processor.waitForCompletion();
2178    }
2179    finally
2180    {
2181      pool.close();
2182
2183      if (rejectWriter != null)
2184      {
2185        try
2186        {
2187          rejectWriter.close();
2188        }
2189        catch (final Exception e)
2190        {
2191          Debug.debugException(e);
2192        }
2193      }
2194    }
2195
2196
2197    // If we've gotten here, then we can consider the command successful, even
2198    // if some of the operations failed.
2199    return ResultCode.SUCCESS;
2200  }
2201
2202
2203
2204  /**
2205   * Retrieves the argument parser for this tool.
2206   *
2207   * @return  The argument parser for this tool.
2208   */
2209  ArgumentParser getArgumentParser()
2210  {
2211    return parser;
2212  }
2213
2214
2215
2216  /**
2217   * Indicates whether the tool should cancel its processing.
2218   *
2219   * @return  {@code true} if the tool should cancel its processing, or
2220   *          {@code false} if not.
2221   */
2222  boolean cancelRequested()
2223  {
2224    return cancelRequested.get();
2225  }
2226
2227
2228
2229  /**
2230   * Indicates whether the manage-account processor has been provided with all
2231   * of the DNs of all of the entries to process.
2232   *
2233   * @return  {@code true} if the manage-account processor has been provided
2234   *          with all of the DNs of all of the entries to process, or
2235   *          {@code false} if not.
2236   */
2237  boolean allDNsProvided()
2238  {
2239    return allDNsProvided.get();
2240  }
2241
2242
2243
2244  /**
2245   * Indicates whether the manage-account search processor has been provided
2246   * with all of the filters to use to identify entries to process.
2247   *
2248   * @return  {@code true} if the manage-account search processor has been
2249   *          provided with all of the filters to use to identify entries to
2250   *          process, or {@code false} if not.
2251   */
2252  boolean allFiltersProvided()
2253  {
2254    return allFiltersProvided.get();
2255  }
2256
2257
2258
2259  /**
2260   * {@inheritDoc}
2261   */
2262  @Override()
2263  protected boolean registerShutdownHook()
2264  {
2265    return true;
2266  }
2267
2268
2269
2270  /**
2271   * {@inheritDoc}
2272   */
2273  @Override()
2274  protected void doShutdownHookProcessing(final ResultCode resultCode)
2275  {
2276    cancelRequested.set(true);
2277
2278    if (rateLimiter != null)
2279    {
2280      rateLimiter.shutdownRequested();
2281    }
2282
2283    if (searchProcessor != null)
2284    {
2285      searchProcessor.cancelSearches();
2286    }
2287  }
2288
2289
2290
2291  /**
2292   * Performs any processing that may be necessary in response to the provided
2293   * unsolicited notification that has been received from the server.
2294   *
2295   * @param connection   The connection on which the unsolicited notification
2296   *                     was received.
2297   * @param notification The unsolicited notification that has been received
2298   *                     from the server.
2299   */
2300  @Override()
2301  public void handleUnsolicitedNotification(final LDAPConnection connection,
2302                                            final ExtendedResult notification)
2303  {
2304    final String message = NOTE_MANAGE_ACCT_UNSOLICITED_NOTIFICATION.get(
2305         String.valueOf(connection), String.valueOf(notification));
2306    if (outputWriter == null)
2307    {
2308      err();
2309      err("* " + message);
2310      err();
2311    }
2312    else
2313    {
2314      try
2315      {
2316        outputWriter.writeComment(message, true, true);
2317        outputWriter.flush();
2318      }
2319      catch (final Exception e)
2320      {
2321        // We can't really do anything about this.
2322        Debug.debugException(e);
2323      }
2324    }
2325  }
2326
2327
2328
2329  /**
2330   * {@inheritDoc}
2331   */
2332  @Override()
2333  public LinkedHashMap<String[],String> getExampleUsages()
2334  {
2335    final LinkedHashMap<String[],String> examples =
2336         new LinkedHashMap<>(StaticUtils.computeMapCapacity(4));
2337
2338    createSubCommandExample(examples,
2339         ManageAccountSubCommandType.GET_ALL,
2340         INFO_MANAGE_ACCT_SC_GET_ALL_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
2341
2342    createSubCommandExample(examples,
2343         ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_ERRORS,
2344         INFO_MANAGE_ACCT_SC_GET_USABILITY_ERRORS_EXAMPLE.get(
2345              EXAMPLE_TARGET_USER_DN));
2346
2347    createSubCommandExample(examples,
2348         ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED,
2349         INFO_MANAGE_ACCT_SC_SET_IS_DISABLED_EXAMPLE.get(
2350              EXAMPLE_TARGET_USER_DN),
2351         "--accountIsDisabled", "true");
2352
2353    createSubCommandExample(examples,
2354         ManageAccountSubCommandType.CLEAR_AUTHENTICATION_FAILURE_TIMES,
2355         INFO_MANAGE_ACCT_SC_CLEAR_AUTH_FAILURE_TIMES_EXAMPLE.get(
2356              EXAMPLE_TARGET_USER_DN));
2357
2358    return examples;
2359  }
2360}