001/*
002 * Copyright 2016-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2016-2018 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 = new LinkedHashMap<>(1);
1657    createSubCommandExample(examples, t, description, args);
1658    return examples;
1659  }
1660
1661
1662
1663  /**
1664   * Adds an example for a specified subcommand to the given map.
1665   *
1666   * @param  examples     The map to which the example should be added.
1667   * @param  t            The subcommand type.
1668   * @param  description  The description to use for the example.
1669   * @param  args         The set of arguments to include in the example,
1670   *                      excluding the subcommand name and the arguments used
1671   *                      to connect and authenticate to the server.  This may
1672   *                      be empty if no additional arguments are needed.
1673   */
1674  private static void createSubCommandExample(
1675       final LinkedHashMap<String[], String> examples,
1676       final ManageAccountSubCommandType t, final String description,
1677       final String... args)
1678  {
1679    final ArrayList<String> argList = new ArrayList<>(10 + args.length);
1680    argList.add(t.getPrimaryName());
1681    argList.add("--hostname");
1682    argList.add("server.example.com");
1683    argList.add("--port");
1684    argList.add("389");
1685    argList.add("--bindDN");
1686    argList.add("uid=admin,dc=example,dc=com");
1687    argList.add("--promptForBindPassword");
1688    argList.add("--targetDN");
1689    argList.add("uid=jdoe,ou=People,dc=example,dc=com");
1690
1691    if (args.length > 0)
1692    {
1693      argList.addAll(Arrays.asList(args));
1694    }
1695
1696    final String[] argArray = new String[argList.size()];
1697    argList.toArray(argArray);
1698
1699    examples.put(argArray, description);
1700  }
1701
1702
1703
1704  /**
1705   * Creates a subcommand with the provided information.
1706   *
1707   * @param  subcommandType       The subcommand type.
1708   * @param  exampleDescription   The description to use for the
1709   *                              automatically-generated example.
1710   *
1711   * @throws  ArgumentException  If a problem is encountered while creating the
1712   *                             subcommand.
1713   */
1714  private void createSubCommand(
1715                    final ManageAccountSubCommandType subcommandType,
1716                    final String exampleDescription)
1717          throws ArgumentException
1718  {
1719    final ArgumentParser subcommandParser =
1720         createSubCommandParser(subcommandType);
1721
1722    final LinkedHashMap<String[],String> examples =
1723         createSubCommandExample(subcommandType, exampleDescription);
1724
1725    createSubCommand(subcommandType, subcommandParser, examples);
1726  }
1727
1728
1729
1730  /**
1731   * Creates a subcommand with the provided information.
1732   *
1733   * @param  subcommandType    The subcommand type.
1734   * @param  subcommandParser  The argument parser for the subcommand-specific
1735   *                           arguments.
1736   * @param  examples          The example usages for the subcommand.
1737   *
1738   * @throws  ArgumentException  If a problem is encountered while creating the
1739   *                             subcommand.
1740   */
1741  private void createSubCommand(
1742                    final ManageAccountSubCommandType subcommandType,
1743                    final ArgumentParser subcommandParser,
1744                    final LinkedHashMap<String[],String> examples)
1745          throws ArgumentException
1746  {
1747    final SubCommand subCommand = new SubCommand(
1748         subcommandType.getPrimaryName(), subcommandType.getDescription(),
1749         subcommandParser, examples);
1750
1751    for (final String alternateName : subcommandType.getAlternateNames())
1752    {
1753      subCommand.addName(alternateName, true);
1754    }
1755
1756    parser.addSubCommand(subCommand);
1757  }
1758
1759
1760
1761  /**
1762   * {@inheritDoc}
1763   */
1764  @Override()
1765  public LDAPConnectionOptions getConnectionOptions()
1766  {
1767    return connectionOptions;
1768  }
1769
1770
1771
1772  /**
1773   * {@inheritDoc}
1774   */
1775  @Override()
1776  public ResultCode doToolProcessing()
1777  {
1778    // If we should just generate a sample rate data file, then do that now.
1779    final FileArgument generateSampleRateFile =
1780         parser.getFileArgument(ARG_GENERATE_SAMPLE_RATE_FILE);
1781    if (generateSampleRateFile.isPresent())
1782    {
1783      try
1784      {
1785        RateAdjustor.writeSampleVariableRateFile(
1786             generateSampleRateFile.getValue());
1787        return ResultCode.SUCCESS;
1788      }
1789      catch (final Exception e)
1790      {
1791        Debug.debugException(e);
1792        wrapErr(0, WRAP_COLUMN,
1793             ERR_MANAGE_ACCT_CANNOT_GENERATE_SAMPLE_RATE_FILE.get(
1794                  generateSampleRateFile.getValue().getAbsolutePath(),
1795                  StaticUtils.getExceptionMessage(e)));
1796        return ResultCode.LOCAL_ERROR;
1797      }
1798    }
1799
1800
1801    // If we need to create a fixed-rate barrier and/or use a variable rate
1802    // definition, then set that up.
1803    final IntegerArgument ratePerSecond =
1804         parser.getIntegerArgument(ARG_RATE_PER_SECOND);
1805    final FileArgument variableRateData =
1806         parser.getFileArgument(ARG_VARIABLE_RATE_DATA);
1807    if (ratePerSecond.isPresent() || variableRateData.isPresent())
1808    {
1809      if (ratePerSecond.isPresent())
1810      {
1811        rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue());
1812      }
1813      else
1814      {
1815        rateLimiter = new FixedRateBarrier(1000L, Integer.MAX_VALUE);
1816      }
1817
1818      if (variableRateData.isPresent())
1819      {
1820        try
1821        {
1822          rateAdjustor = RateAdjustor.newInstance(rateLimiter,
1823               ratePerSecond.getValue(), variableRateData.getValue());
1824        }
1825        catch (final Exception e)
1826        {
1827          Debug.debugException(e);
1828          wrapErr(0, WRAP_COLUMN,
1829               ERR_MANAGE_ACCT_CANNOT_CREATE_RATE_ADJUSTOR.get(
1830                    variableRateData.getValue().getAbsolutePath(),
1831                    StaticUtils.getExceptionMessage(e)));
1832          return ResultCode.PARAM_ERROR;
1833        }
1834      }
1835    }
1836
1837
1838    // Create the connection pool to use for all processing.
1839    final LDAPConnectionPool pool;
1840    final int numSearchThreads =
1841         parser.getIntegerArgument(ARG_NUM_SEARCH_THREADS).getValue();
1842    try
1843    {
1844      final int numOperationThreads =
1845           parser.getIntegerArgument(ARG_NUM_THREADS).getValue();
1846      pool = getConnectionPool(numOperationThreads,
1847           (numOperationThreads + numSearchThreads));
1848
1849      // Explicitly disable automatic retry, since it probably won't work
1850      // reliably for extended operations anyway.  We'll handle retry manually.
1851      pool.setRetryFailedOperationsDueToInvalidConnections(false);
1852
1853      // Set a maximum connection age of 30 minutes.
1854      pool.setMaxConnectionAgeMillis(1_800_000L);
1855    }
1856    catch (final LDAPException le)
1857    {
1858      Debug.debugException(le);
1859
1860      wrapErr(0, WRAP_COLUMN,
1861           ERR_MANAGE_ACCT_CANNOT_CREATE_CONNECTION_POOL.get(getToolName(),
1862                le.getMessage()));
1863      return le.getResultCode();
1864    }
1865
1866
1867    try
1868    {
1869      // Create the output writer.  This should always succeed.
1870      outputWriter = new LDIFWriter(getOut());
1871
1872
1873
1874      // Create the reject writer if appropriate.
1875      final FileArgument rejectFile = parser.getFileArgument(ARG_REJECT_FILE);
1876      if (rejectFile.isPresent())
1877      {
1878        final BooleanArgument appendToRejectFile =
1879             parser.getBooleanArgument(ARG_APPEND_TO_REJECT_FILE);
1880
1881        try
1882        {
1883          rejectWriter = new LDIFWriter(new FileOutputStream(
1884               rejectFile.getValue(), appendToRejectFile.isPresent()));
1885        }
1886        catch (final Exception e)
1887        {
1888          Debug.debugException(e);
1889          wrapErr(0, WRAP_COLUMN,
1890               ERR_MANAGE_ACCT_CANNOT_CREATE_REJECT_WRITER.get(
1891                    rejectFile.getValue().getAbsolutePath(),
1892                    StaticUtils.getExceptionMessage(e)));
1893          return ResultCode.LOCAL_ERROR;
1894        }
1895      }
1896
1897
1898      // Create the processor that will be used to actually perform the
1899      // manage-account operation processing for each entry.
1900      final ManageAccountProcessor processor;
1901      try
1902      {
1903        processor = new ManageAccountProcessor(this, pool, rateLimiter,
1904             outputWriter, rejectWriter);
1905      }
1906      catch (final LDAPException le)
1907      {
1908        Debug.debugException(le);
1909        wrapErr(0, WRAP_COLUMN,
1910             ERR_MANAGE_ACCT_CANNOT_CREATE_PROCESSOR.get(
1911                  StaticUtils.getExceptionMessage(le)));
1912        return le.getResultCode();
1913      }
1914
1915
1916      // If we should use a rate adjustor, then start it now.
1917      if (rateAdjustor != null)
1918      {
1919        rateAdjustor.start();
1920      }
1921
1922
1923      // If any targetDN values were provided, then process them now.
1924      final DNArgument targetDN = parser.getDNArgument(ARG_TARGET_DN);
1925      if (targetDN.isPresent())
1926      {
1927        for (final DN dn : targetDN.getValues())
1928        {
1929          if (cancelRequested())
1930          {
1931            return ResultCode.USER_CANCELED;
1932          }
1933
1934          processor.process(dn.toString());
1935        }
1936      }
1937
1938
1939      // If any DN input files were specified, then process them now.
1940      final FileArgument dnInputFile =
1941           parser.getFileArgument(ARG_DN_INPUT_FILE);
1942      if (dnInputFile.isPresent())
1943      {
1944        for (final File f : dnInputFile.getValues())
1945        {
1946          DNFileReader reader = null;
1947          try
1948          {
1949            reader = new DNFileReader(f);
1950            while (true)
1951            {
1952              if (cancelRequested())
1953              {
1954                return ResultCode.USER_CANCELED;
1955              }
1956
1957              final DN dn;
1958              try
1959              {
1960                dn = reader.readDN();
1961              }
1962              catch (final LDAPException le)
1963              {
1964                Debug.debugException(le);
1965                processor.handleMessage(le.getMessage(), true);
1966                continue;
1967              }
1968
1969              if (dn == null)
1970              {
1971                break;
1972              }
1973
1974              processor.process(dn.toString());
1975            }
1976          }
1977          catch (final Exception e)
1978          {
1979            Debug.debugException(e);
1980            processor.handleMessage(
1981                 ERR_MANAGE_ACCT_ERROR_READING_DN_FILE.get(
1982                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
1983                 true);
1984          }
1985          finally
1986          {
1987            if (reader != null)
1988            {
1989              try
1990              {
1991                reader.close();
1992              }
1993              catch (final Exception e2)
1994              {
1995                Debug.debugException(e2);
1996              }
1997            }
1998          }
1999        }
2000      }
2001
2002
2003      // If any target filters were specified, then process them now.
2004      final FilterArgument targetFilter =
2005           parser.getFilterArgument(ARG_TARGET_FILTER);
2006      if (targetFilter.isPresent())
2007      {
2008        searchProcessor =
2009             new ManageAccountSearchProcessor(this, processor, pool);
2010        for (final Filter f : targetFilter.getValues())
2011        {
2012          searchProcessor.processFilter(f);
2013        }
2014      }
2015
2016
2017      // If any filter input files were specified, then process them now.
2018      final FileArgument filterInputFile =
2019           parser.getFileArgument(ARG_FILTER_INPUT_FILE);
2020      if (filterInputFile.isPresent())
2021      {
2022        if (searchProcessor == null)
2023        {
2024          searchProcessor =
2025               new ManageAccountSearchProcessor(this, processor, pool);
2026        }
2027
2028        for (final File f : filterInputFile.getValues())
2029        {
2030          FilterFileReader reader = null;
2031          try
2032          {
2033            reader = new FilterFileReader(f);
2034            while (true)
2035            {
2036              if (cancelRequested())
2037              {
2038                return ResultCode.USER_CANCELED;
2039              }
2040
2041              final Filter filter;
2042              try
2043              {
2044                filter = reader.readFilter();
2045              }
2046              catch (final LDAPException le)
2047              {
2048                Debug.debugException(le);
2049                processor.handleMessage(le.getMessage(), true);
2050                continue;
2051              }
2052
2053              if (filter == null)
2054              {
2055                break;
2056              }
2057
2058              searchProcessor.processFilter(filter);
2059            }
2060          }
2061          catch (final Exception e)
2062          {
2063            Debug.debugException(e);
2064            processor.handleMessage(
2065                 ERR_MANAGE_ACCT_ERROR_READING_FILTER_FILE.get(
2066                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
2067                 true);
2068          }
2069          finally
2070          {
2071            if (reader != null)
2072            {
2073              try
2074              {
2075                reader.close();
2076              }
2077              catch (final Exception e2)
2078              {
2079                Debug.debugException(e2);
2080              }
2081            }
2082          }
2083        }
2084      }
2085
2086
2087      // If any target user IDs were specified, then process them now.
2088      final StringArgument targetUserID =
2089           parser.getStringArgument(ARG_TARGET_USER_ID);
2090      if (targetUserID.isPresent())
2091      {
2092        if (searchProcessor == null)
2093        {
2094          searchProcessor =
2095               new ManageAccountSearchProcessor(this, processor, pool);
2096        }
2097
2098        for (final String userID : targetUserID.getValues())
2099        {
2100          searchProcessor.processUserID(userID);
2101        }
2102      }
2103
2104
2105      // If any user ID input files were specified, then process them now.
2106      final FileArgument userIDInputFile =
2107           parser.getFileArgument(ARG_USER_ID_INPUT_FILE);
2108      if (userIDInputFile.isPresent())
2109      {
2110        if (searchProcessor == null)
2111        {
2112          searchProcessor =
2113               new ManageAccountSearchProcessor(this, processor, pool);
2114        }
2115
2116        for (final File f : userIDInputFile.getValues())
2117        {
2118          BufferedReader reader = null;
2119          try
2120          {
2121            reader = new BufferedReader(new FileReader(f));
2122            while (true)
2123            {
2124              if (cancelRequested())
2125              {
2126                return ResultCode.USER_CANCELED;
2127              }
2128
2129              final String line = reader.readLine();
2130              if (line == null)
2131              {
2132                break;
2133              }
2134
2135              if ((line.length() == 0) || line.startsWith("#"))
2136              {
2137                continue;
2138              }
2139
2140              searchProcessor.processUserID(line.trim());
2141            }
2142          }
2143          catch (final Exception e)
2144          {
2145            Debug.debugException(e);
2146            processor.handleMessage(
2147                 ERR_MANAGE_ACCT_ERROR_READING_USER_ID_FILE.get(
2148                      f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)),
2149                 true);
2150          }
2151          finally
2152          {
2153            if (reader != null)
2154            {
2155              try
2156              {
2157                reader.close();
2158              }
2159              catch (final Exception e2)
2160              {
2161                Debug.debugException(e2);
2162              }
2163            }
2164          }
2165        }
2166      }
2167
2168
2169      allFiltersProvided.set(true);
2170      if (searchProcessor != null)
2171      {
2172        searchProcessor.waitForCompletion();
2173      }
2174
2175      allDNsProvided.set(true);
2176      processor.waitForCompletion();
2177    }
2178    finally
2179    {
2180      pool.close();
2181
2182      if (rejectWriter != null)
2183      {
2184        try
2185        {
2186          rejectWriter.close();
2187        }
2188        catch (final Exception e)
2189        {
2190          Debug.debugException(e);
2191        }
2192      }
2193    }
2194
2195
2196    // If we've gotten here, then we can consider the command successful, even
2197    // if some of the operations failed.
2198    return ResultCode.SUCCESS;
2199  }
2200
2201
2202
2203  /**
2204   * Retrieves the argument parser for this tool.
2205   *
2206   * @return  The argument parser for this tool.
2207   */
2208  ArgumentParser getArgumentParser()
2209  {
2210    return parser;
2211  }
2212
2213
2214
2215  /**
2216   * Indicates whether the tool should cancel its processing.
2217   *
2218   * @return  {@code true} if the tool should cancel its processing, or
2219   *          {@code false} if not.
2220   */
2221  boolean cancelRequested()
2222  {
2223    return cancelRequested.get();
2224  }
2225
2226
2227
2228  /**
2229   * Indicates whether the manage-account processor has been provided with all
2230   * of the DNs of all of the entries to process.
2231   *
2232   * @return  {@code true} if the manage-account processor has been provided
2233   *          with all of the DNs of all of the entries to process, or
2234   *          {@code false} if not.
2235   */
2236  boolean allDNsProvided()
2237  {
2238    return allDNsProvided.get();
2239  }
2240
2241
2242
2243  /**
2244   * Indicates whether the manage-account search processor has been provided
2245   * with all of the filters to use to identify entries to process.
2246   *
2247   * @return  {@code true} if the manage-account search processor has been
2248   *          provided with all of the filters to use to identify entries to
2249   *          process, or {@code false} if not.
2250   */
2251  boolean allFiltersProvided()
2252  {
2253    return allFiltersProvided.get();
2254  }
2255
2256
2257
2258  /**
2259   * {@inheritDoc}
2260   */
2261  @Override()
2262  protected boolean registerShutdownHook()
2263  {
2264    return true;
2265  }
2266
2267
2268
2269  /**
2270   * {@inheritDoc}
2271   */
2272  @Override()
2273  protected void doShutdownHookProcessing(final ResultCode resultCode)
2274  {
2275    cancelRequested.set(true);
2276
2277    if (rateLimiter != null)
2278    {
2279      rateLimiter.shutdownRequested();
2280    }
2281
2282    if (searchProcessor != null)
2283    {
2284      searchProcessor.cancelSearches();
2285    }
2286  }
2287
2288
2289
2290  /**
2291   * Performs any processing that may be necessary in response to the provided
2292   * unsolicited notification that has been received from the server.
2293   *
2294   * @param connection   The connection on which the unsolicited notification
2295   *                     was received.
2296   * @param notification The unsolicited notification that has been received
2297   *                     from the server.
2298   */
2299  @Override()
2300  public void handleUnsolicitedNotification(final LDAPConnection connection,
2301                                            final ExtendedResult notification)
2302  {
2303    final String message = NOTE_MANAGE_ACCT_UNSOLICITED_NOTIFICATION.get(
2304         String.valueOf(connection), String.valueOf(notification));
2305    if (outputWriter == null)
2306    {
2307      err();
2308      err("* " + message);
2309      err();
2310    }
2311    else
2312    {
2313      try
2314      {
2315        outputWriter.writeComment(message, true, true);
2316        outputWriter.flush();
2317      }
2318      catch (final Exception e)
2319      {
2320        // We can't really do anything about this.
2321        Debug.debugException(e);
2322      }
2323    }
2324  }
2325
2326
2327
2328  /**
2329   * {@inheritDoc}
2330   */
2331  @Override()
2332  public LinkedHashMap<String[],String> getExampleUsages()
2333  {
2334    final LinkedHashMap<String[],String> examples = new LinkedHashMap<>(4);
2335
2336    createSubCommandExample(examples,
2337         ManageAccountSubCommandType.GET_ALL,
2338         INFO_MANAGE_ACCT_SC_GET_ALL_EXAMPLE.get(EXAMPLE_TARGET_USER_DN));
2339
2340    createSubCommandExample(examples,
2341         ManageAccountSubCommandType.GET_ACCOUNT_USABILITY_ERRORS,
2342         INFO_MANAGE_ACCT_SC_GET_USABILITY_ERRORS_EXAMPLE.get(
2343              EXAMPLE_TARGET_USER_DN));
2344
2345    createSubCommandExample(examples,
2346         ManageAccountSubCommandType.SET_ACCOUNT_IS_DISABLED,
2347         INFO_MANAGE_ACCT_SC_SET_IS_DISABLED_EXAMPLE.get(
2348              EXAMPLE_TARGET_USER_DN),
2349         "--accountIsDisabled", "true");
2350
2351    createSubCommandExample(examples,
2352         ManageAccountSubCommandType.CLEAR_AUTHENTICATION_FAILURE_TIMES,
2353         INFO_MANAGE_ACCT_SC_CLEAR_AUTH_FAILURE_TIMES_EXAMPLE.get(
2354              EXAMPLE_TARGET_USER_DN));
2355
2356    return examples;
2357  }
2358}