001/*
002 * Copyright 2015-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-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.extensions;
022
023
024
025import java.util.ArrayList;
026import java.util.Collections;
027import java.util.Iterator;
028import java.util.List;
029
030import com.unboundid.asn1.ASN1Element;
031import com.unboundid.asn1.ASN1OctetString;
032import com.unboundid.asn1.ASN1Sequence;
033import com.unboundid.ldap.sdk.Control;
034import com.unboundid.ldap.sdk.ExtendedRequest;
035import com.unboundid.ldap.sdk.ExtendedResult;
036import com.unboundid.ldap.sdk.LDAPConnection;
037import com.unboundid.ldap.sdk.LDAPException;
038import com.unboundid.ldap.sdk.ResultCode;
039import com.unboundid.util.Debug;
040import com.unboundid.util.NotMutable;
041import com.unboundid.util.ObjectPair;
042import com.unboundid.util.StaticUtils;
043import com.unboundid.util.ThreadSafety;
044import com.unboundid.util.ThreadSafetyLevel;
045import com.unboundid.util.Validator;
046
047import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
048
049
050
051/**
052 * This class provides an implementation of an extended request that can be used
053 * to trigger the delivery of a temporary one-time password reset token to a
054 * specified user.  This token can be provided to the password modify extended
055 * request in lieu of the current password for the purpose of performing a self
056 * change and setting a new password.  This token cannot be used to authenticate
057 * to the server in any other way, and it can only be used once.  The token will
058 * expire after a short period of time, and any attempt to use it after its
059 * expiration will fail.  In addition, because this token is only intended for
060 * use in the event that the current password cannot be used (e.g., because it
061 * has been forgotten or the account is locked), a successful bind with the
062 * current password will cause the server to invalidate any password reset token
063 * for that user.
064 * <BR>
065 * <BLOCKQUOTE>
066 *   <B>NOTE:</B>  This class, and other classes within the
067 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
068 *   supported for use against Ping Identity, UnboundID, and Alcatel-Lucent 8661
069 *   server products.  These classes provide support for proprietary
070 *   functionality or for external specifications that are not considered stable
071 *   or mature enough to be guaranteed to work in an interoperable way with
072 *   other types of LDAP servers.
073 * </BLOCKQUOTE>
074 * <BR>
075 * The server will use the same mechanisms for delivering password reset tokens
076 * as it uses for delivering one-time passwords via the
077 * {@link DeliverOneTimePasswordExtendedRequest}.  See the
078 * ds-supported-otp-delivery-mechanism attribute in the root DSE for a list of
079 * the one-time password delivery mechanisms that are configured for use in the
080 * server.
081 * <BR><BR>
082 * This extended request is expected to be used to help applications provide a
083 * secure, automated password reset feature.  In the event that a user has
084 * forgotten his/her password, has allowed the password to expire, or has
085 * allowed the account to become locked, the application can collect a
086 * sufficient set of information to identify the user and request that the
087 * server generate and deliver the password reset token to the end user.
088 * <BR><BR>
089 * The OID for this extended request is 1.3.6.1.4.1.30221.2.6.45.  It must have
090 * a value with the following encoding:
091 * <PRE>
092 *   DeliverPasswordResetTokenRequestValue ::= SEQUENCE {
093 *        userDN                         LDAPDN,
094 *        messageSubject                 [0] OCTET STRING OPTIONAL,
095 *        fullTextBeforeToken            [1] OCTET STRING OPTIONAL,
096 *        fullTextAfterToken             [2] OCTET STRING OPTIONAL,
097 *        compactTextBeforeToken         [3] OCTET STRING OPTIONAL,
098 *        compactTextAfterToken          [4] OCTET STRING OPTIONAL,
099 *        preferredDeliveryMechanism     [5] SEQUENCE OF SEQUENCE {
100 *             mechanismName     OCTET STRING,
101 *             recipientID       OCTET STRING OPTIONAL },
102 *        ... }
103 * </PRE>
104 *
105 * @see  DeliverPasswordResetTokenExtendedResult
106 */
107@NotMutable()
108@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
109public final class DeliverPasswordResetTokenExtendedRequest
110       extends ExtendedRequest
111{
112  /**
113   * The OID (1.3.6.1.4.1.30221.2.6.45) for the deliver password reset token
114   * extended request.
115   */
116  public static final String DELIVER_PW_RESET_TOKEN_REQUEST_OID =
117       "1.3.6.1.4.1.30221.2.6.45";
118
119
120
121  /**
122   * The BER type for the "message subject" element of the value sequence.
123   */
124  private static final byte MESSAGE_SUBJECT_BER_TYPE = (byte) 0x80;
125
126
127
128  /**
129   * The BER type for the "full text before token" element of the value
130   * sequence.
131   */
132  private static final byte FULL_TEXT_BEFORE_TOKEN_BER_TYPE = (byte) 0x81;
133
134
135
136  /**
137   * The BER type for the "full text after token" element of the value
138   * sequence.
139   */
140  private static final byte FULL_TEXT_AFTER_TOKEN_BER_TYPE = (byte) 0x82;
141
142
143
144  /**
145   * The BER type for the "compact text before token" element of the value
146   * sequence.
147   */
148  private static final byte COMPACT_TEXT_BEFORE_TOKEN_BER_TYPE = (byte) 0x83;
149
150
151
152  /**
153   * The BER type for the "compact text after token" element of the value
154   * sequence.
155   */
156  private static final byte COMPACT_TEXT_AFTER_TOKEN_BER_TYPE = (byte) 0x84;
157
158
159
160  /**
161   * The BER type for the "preferred delivery mechanism" element of the value
162   * sequence.
163   */
164  private static final byte PREFERRED_DELIVERY_MECHANISM_BER_TYPE = (byte) 0xA5;
165
166
167
168  /**
169   * The serial version UID for this serializable class.
170   */
171  private static final long serialVersionUID = 7608072810737347230L;
172
173
174
175  // An ordered list of the preferred delivery mechanisms for the token,
176  // paired with an optional recipient ID for each mechanism.
177  private final List<ObjectPair<String, String>> preferredDeliveryMechanisms;
178
179  // The text to include after the token in a compact message.
180  private final String compactTextAfterToken;
181
182  // The text to include before the token in a compact message.
183  private final String compactTextBeforeToken;
184
185  // The text to include after the token in a message without size constraints.
186  private final String fullTextAfterToken;
187
188  // The text to include before the token in a message without size constraints.
189  private final String fullTextBeforeToken;
190
191  // The text to use as the message subject.
192  private final String messageSubject;
193
194  // The DN of the user to whom the password reset token should be delivered.
195  private final String userDN;
196
197
198
199  /**
200   * Creates a new deliver password reset token extended request with the
201   * provided information.
202   *
203   * @param  userDN                       The DN of the user to whom the
204   *                                      password reset token should be
205   *                                      generated.
206   * @param  preferredDeliveryMechanisms  An optional ordered list of preferred
207   *                                      delivery mechanisms that should be
208   *                                      used to deliver the token to the user.
209   *                                      It may be {@code null} or empty to
210   *                                      allow the server to select an
211   *                                      appropriate delivery mechanism.  If it
212   *                                      is non-{@code null} and non-empty,
213   *                                      then only the listed mechanisms will
214   *                                      be considered for use, even if the
215   *                                      server supports alternate mechanisms
216   *                                      not included in this list.
217   */
218  public DeliverPasswordResetTokenExtendedRequest(final String userDN,
219              final String... preferredDeliveryMechanisms)
220  {
221    this(userDN, preferredMechanismsToList(preferredDeliveryMechanisms));
222  }
223
224
225
226  /**
227   * Creates a new deliver password reset token extended request with the
228   * provided information.
229   *
230   * @param  userDN                       The DN of the user to whom the
231   *                                      password reset token should be
232   *                                      generated.
233   * @param  preferredDeliveryMechanisms  An optional ordered list of preferred
234   *                                      delivery mechanisms that should be
235   *                                      used to deliver the token to the user.
236   *                                      It may be {@code null} or empty to
237   *                                      allow the server to select an
238   *                                      appropriate delivery mechanism.  If it
239   *                                      is non-{@code null} and non-empty,
240   *                                      then only the listed mechanisms will
241   *                                      be considered for use, even if the
242   *                                      server supports alternate mechanisms
243   *                                      not included in this list.  Each
244   *                                      {@code ObjectPair} item must have
245   *                                      a non-{@code null} value for the first
246   *                                      element, which is the name of the
247   *                                      target delivery mechanism.  It may
248   *                                      optionally have a non-{@code null}
249   *                                      value for the second element, which is
250   *                                      a recipient ID to use for that
251   *                                      mechanism (e.g., the target  mobile
252   *                                      phone number for SMS delivery, an
253   *                                      email address for email delivery,
254   *                                      etc.).  If no recipient ID is provided
255   *                                      for a mechanism, then the server will
256   *                                      attempt to select a value for the
257   *                                      user.
258   * @param  controls                     An optional set of controls to include
259   *                                      in the request.  It may be
260   *                                      {@code null} or empty if no controls
261   *                                      should be included in the request.
262   */
263  public DeliverPasswordResetTokenExtendedRequest(final String userDN,
264              final List<ObjectPair<String,String>> preferredDeliveryMechanisms,
265              final Control... controls)
266  {
267    this(userDN, null, null, null, null, null, preferredDeliveryMechanisms,
268         controls);
269  }
270
271
272
273  /**
274   * Creates a new deliver password reset token extended request with the
275   * provided information.
276   *
277   * @param  userDN                       The DN of the user to whom the
278   *                                      password reset token should be
279   *                                      generated.
280   * @param  messageSubject               The text (if any) that should be used
281   *                                      as the message subject if the delivery
282   *                                      mechanism accepts a subject.  This may
283   *                                      be {@code null} if no subject is
284   *                                      required or a subject should be
285   *                                      automatically generated.
286   * @param  fullTextBeforeToken          The text (if any) that should appear
287   *                                      before the generated password reset
288   *                                      token in the message delivered to the
289   *                                      user via a delivery mechanism that
290   *                                      does not impose significant
291   *                                      constraints on message size.  This may
292   *                                      be {@code null} if no text is required
293   *                                      before the token.
294   * @param  fullTextAfterToken           The text (if any) that should appear
295   *                                      after the generated password reset
296   *                                      token in the message delivered to the
297   *                                      user via a delivery mechanism that
298   *                                      does not impose significant
299   *                                      constraints on message size.  This may
300   *                                      be {@code null} if no text is required
301   *                                      after the token.
302   * @param  compactTextBeforeToken       The text (if any) that should appear
303   *                                      before the generated password reset
304   *                                      token in the message delivered to the
305   *                                      user via a delivery mechanism that
306   *                                      imposes significant constraints on
307   *                                      message size.  This may be
308   *                                      {@code null} if no text is required
309   *                                      before the token.
310   * @param  compactTextAfterToken        The text (if any) that should appear
311   *                                      after the generated password reset
312   *                                      token in the message delivered to the
313   *                                      user via a delivery mechanism that
314   *                                      imposes significant constraints on
315   *                                      message size.  This may be
316   *                                      {@code null} if no text is required
317   *                                      after the token.
318   * @param  preferredDeliveryMechanisms  An optional ordered list of preferred
319   *                                      delivery mechanisms that should be
320   *                                      used to deliver the token to the user.
321   *                                      It may be {@code null} or empty to
322   *                                      allow the server to select an
323   *                                      appropriate delivery mechanism.  If it
324   *                                      is non-{@code null} and non-empty,
325   *                                      then only the listed mechanisms will
326   *                                      be considered for use, even if the
327   *                                      server supports alternate mechanisms
328   *                                      not included in this list.  Each
329   *                                      {@code ObjectPair} item must have
330   *                                      a non-{@code null} value for the first
331   *                                      element, which is the name of the
332   *                                      target delivery mechanism.  It may
333   *                                      optionally have a non-{@code null}
334   *                                      value for the second element, which is
335   *                                      a recipient ID to use for that
336   *                                      mechanism (e.g., the target  mobile
337   *                                      phone number for SMS delivery, an
338   *                                      email address for email delivery,
339   *                                      etc.).  If no recipient ID is provided
340   *                                      for a mechanism, then the server will
341   *                                      attempt to select a value for the
342   *                                      user.
343   * @param  controls                     An optional set of controls to include
344   *                                      in the request.  It may be
345   *                                      {@code null} or empty if no controls
346   *                                      should be included in the request.
347   */
348  public DeliverPasswordResetTokenExtendedRequest(final String userDN,
349              final String messageSubject, final String fullTextBeforeToken,
350              final String fullTextAfterToken,
351              final String compactTextBeforeToken,
352              final String compactTextAfterToken,
353              final List<ObjectPair<String,String>> preferredDeliveryMechanisms,
354              final Control... controls)
355  {
356    super(DELIVER_PW_RESET_TOKEN_REQUEST_OID,
357         encodeValue(userDN, messageSubject, fullTextBeforeToken,
358              fullTextAfterToken, compactTextBeforeToken, compactTextAfterToken,
359              preferredDeliveryMechanisms), controls);
360
361    this.userDN                 = userDN;
362    this.messageSubject         = messageSubject;
363    this.fullTextBeforeToken    = fullTextBeforeToken;
364    this.fullTextAfterToken     = fullTextAfterToken;
365    this.compactTextBeforeToken = compactTextBeforeToken;
366    this.compactTextAfterToken  = compactTextAfterToken;
367
368    if (preferredDeliveryMechanisms == null)
369    {
370      this.preferredDeliveryMechanisms = Collections.emptyList();
371    }
372    else
373    {
374      this.preferredDeliveryMechanisms = Collections.unmodifiableList(
375           new ArrayList<ObjectPair<String,String>>(
376                preferredDeliveryMechanisms));
377    }
378  }
379
380
381
382  /**
383   * Creates a new deliver password reset token extended request that is decoded
384   * from the provided extended request.
385   *
386   * @param  request  The generic extended request to decode as a deliver
387   *                  password reset token request.  It must not be
388   *                  {@code null}.
389   *
390   * @throws  LDAPException  If an unexpected problem occurs.
391   */
392  public DeliverPasswordResetTokenExtendedRequest(final ExtendedRequest request)
393         throws LDAPException
394  {
395    super(request);
396
397    final ASN1OctetString value = request.getValue();
398    if (value == null)
399    {
400      throw new LDAPException(ResultCode.DECODING_ERROR,
401           ERR_DELIVER_PW_RESET_TOKEN_REQUEST_NO_VALUE.get());
402    }
403
404    try
405    {
406      final ASN1Element[] elements =
407           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
408      userDN = ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
409
410      String subject = null;
411      String fullBefore = null;
412      String fullAfter = null;
413      String compactBefore = null;
414      String compactAfter = null;
415      final ArrayList<ObjectPair<String,String>> pdmList =
416           new ArrayList<ObjectPair<String,String>>(10);
417      for (int i=1; i < elements.length; i++)
418      {
419        switch (elements[i].getType())
420        {
421          case MESSAGE_SUBJECT_BER_TYPE:
422            subject =
423                 ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
424            break;
425
426          case FULL_TEXT_BEFORE_TOKEN_BER_TYPE:
427            fullBefore =
428                 ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
429            break;
430
431          case FULL_TEXT_AFTER_TOKEN_BER_TYPE:
432            fullAfter =
433                 ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
434            break;
435
436          case COMPACT_TEXT_BEFORE_TOKEN_BER_TYPE:
437            compactBefore =
438                 ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
439            break;
440
441          case COMPACT_TEXT_AFTER_TOKEN_BER_TYPE:
442            compactAfter =
443                 ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
444            break;
445
446          case PREFERRED_DELIVERY_MECHANISM_BER_TYPE:
447            final ASN1Element[] pdmElements =
448                 ASN1Sequence.decodeAsSequence(elements[i]).elements();
449            for (final ASN1Element e : pdmElements)
450            {
451              final ASN1Element[] mechElements =
452                   ASN1Sequence.decodeAsSequence(e).elements();
453              final String mech = ASN1OctetString.decodeAsOctetString(
454                   mechElements[0]).stringValue();
455
456              final String recipientID;
457              if (mechElements.length > 1)
458              {
459                recipientID = ASN1OctetString.decodeAsOctetString(
460                     mechElements[1]).stringValue();
461              }
462              else
463              {
464                recipientID = null;
465              }
466
467              pdmList.add(new ObjectPair<String,String>(mech, recipientID));
468            }
469            break;
470
471          default:
472            throw new LDAPException(ResultCode.DECODING_ERROR,
473                 ERR_DELIVER_PW_RESET_TOKEN_REQUEST_UNEXPECTED_TYPE.get(
474                      StaticUtils.toHex(elements[i].getType())));
475        }
476      }
477
478      preferredDeliveryMechanisms = Collections.unmodifiableList(pdmList);
479      messageSubject              = subject;
480      fullTextBeforeToken         = fullBefore;
481      fullTextAfterToken          = fullAfter;
482      compactTextBeforeToken      = compactBefore;
483      compactTextAfterToken       = compactAfter;
484    }
485    catch (final LDAPException le)
486    {
487      Debug.debugException(le);
488      throw le;
489    }
490    catch (final Exception e)
491    {
492      Debug.debugException(e);
493      throw new LDAPException(ResultCode.DECODING_ERROR,
494           ERR_DELIVER_PW_RESET_TOKEN_REQUEST_ERROR_DECODING_VALUE.get(
495                StaticUtils.getExceptionMessage(e)),
496           e);
497    }
498  }
499
500
501
502  /**
503   * Encodes the provided set of preferred delivery mechanisms into a form
504   * acceptable to the constructor that expects an object pair.  All of the
505   * recipient IDs will be {@code null}.
506   *
507   * @param  preferredDeliveryMechanisms  An optional ordered list of preferred
508   *                                      delivery mechanisms that should be
509   *                                      used to deliver the token to the user.
510   *                                      It may be {@code null} or empty to
511   *                                      allow the server to select an
512   *                                      appropriate delivery mechanism.  If it
513   *                                      is non-{@code null} and non-empty,
514   *                                      then only the listed mechanisms will
515   *                                      be considered for use, even if the
516   *                                      server supports alternate mechanisms
517   *                                      not included in this list.
518   *
519   * @return  The resulting list of preferred delivery mechanisms with
520   *          {@code null} recipient IDs.
521   */
522  private static List<ObjectPair<String,String>> preferredMechanismsToList(
523                      final String... preferredDeliveryMechanisms)
524  {
525    if (preferredDeliveryMechanisms == null)
526    {
527      return null;
528    }
529
530    final ArrayList<ObjectPair<String,String>> l =
531         new ArrayList<ObjectPair<String,String>>(
532              preferredDeliveryMechanisms.length);
533    for (final String s : preferredDeliveryMechanisms)
534    {
535      l.add(new ObjectPair<String,String>(s, null));
536    }
537    return l;
538  }
539
540
541
542  /**
543   * Creates an ASN.1 octet string suitable for use as the value of this
544   * extended request.
545   *
546   * @param  userDN                       The DN of the user to whom the
547   *                                      password reset token should be
548   *                                      generated.
549   * @param  messageSubject               The text (if any) that should be used
550   *                                      as the message subject if the delivery
551   *                                      mechanism accepts a subject.  This may
552   *                                      be {@code null} if no subject is
553   *                                      required or a subject should be
554   *                                      automatically generated.
555   * @param  fullTextBeforeToken          The text (if any) that should appear
556   *                                      before the generated password reset
557   *                                      token in the message delivered to the
558   *                                      user via a delivery mechanism that
559   *                                      does not impose significant
560   *                                      constraints on message size.  This may
561   *                                      be {@code null} if no text is required
562   *                                      before the token.
563   * @param  fullTextAfterToken           The text (if any) that should appear
564   *                                      after the generated password reset
565   *                                      token in the message delivered to the
566   *                                      user via a delivery mechanism that
567   *                                      does not impose significant
568   *                                      constraints on message size.  This may
569   *                                      be {@code null} if no text is required
570   *                                      after the token.
571   * @param  compactTextBeforeToken       The text (if any) that should appear
572   *                                      before the generated password reset
573   *                                      token in the message delivered to the
574   *                                      user via a delivery mechanism that
575   *                                      imposes significant constraints on
576   *                                      message size.  This may be
577   *                                      {@code null} if no text is required
578   *                                      before the token.
579   * @param  compactTextAfterToken        The text (if any) that should appear
580   *                                      after the generated password reset
581   *                                      token in the message delivered to the
582   *                                      user via a delivery mechanism that
583   *                                      imposes significant constraints on
584   *                                      message size.  This may be
585   *                                      {@code null} if no text is required
586   *                                      after the token.
587   * @param  preferredDeliveryMechanisms  An optional ordered list of preferred
588   *                                      delivery mechanisms that should be
589   *                                      used to deliver the token to the user.
590   *                                      It may be {@code null} or empty to
591   *                                      allow the server to select an
592   *                                      appropriate delivery mechanism.  If it
593   *                                      is non-{@code null} and non-empty,
594   *                                      then only the listed mechanisms will
595   *                                      be considered for use, even if the
596   *                                      server supports alternate mechanisms
597   *                                      not included in this list.  Each
598   *                                      {@code ObjectPair} item must have
599   *                                      a non-{@code null} value for the first
600   *                                      element, which is the name of the
601   *                                      target delivery mechanism.  It may
602   *                                      optionally have a non-{@code null}
603   *                                      value for the second element, which is
604   *                                      a recipient ID to use for that
605   *                                      mechanism (e.g., the target  mobile
606   *                                      phone number for SMS delivery, an
607   *                                      email address for email delivery,
608   *                                      etc.).  If no recipient ID is provided
609   *                                      for a mechanism, then the server will
610   *                                      attempt to select a value for the
611   *                                      user.
612   *
613   * @return  The ASN.1 octet string containing the encoded request value.
614   */
615  private static ASN1OctetString encodeValue(final String userDN,
616       final String messageSubject, final String fullTextBeforeToken,
617       final String fullTextAfterToken, final String compactTextBeforeToken,
618       final String compactTextAfterToken,
619       final List<ObjectPair<String,String>> preferredDeliveryMechanisms)
620  {
621    Validator.ensureNotNull(userDN);
622
623    final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(7);
624    elements.add(new ASN1OctetString(userDN));
625
626    if (messageSubject != null)
627    {
628      elements.add(new ASN1OctetString(MESSAGE_SUBJECT_BER_TYPE,
629           messageSubject));
630    }
631
632    if (fullTextBeforeToken != null)
633    {
634      elements.add(new ASN1OctetString(FULL_TEXT_BEFORE_TOKEN_BER_TYPE,
635           fullTextBeforeToken));
636    }
637
638    if (fullTextAfterToken != null)
639    {
640      elements.add(new ASN1OctetString(FULL_TEXT_AFTER_TOKEN_BER_TYPE,
641           fullTextAfterToken));
642    }
643
644    if (compactTextBeforeToken != null)
645    {
646      elements.add(new ASN1OctetString(COMPACT_TEXT_BEFORE_TOKEN_BER_TYPE,
647           compactTextBeforeToken));
648    }
649
650    if (compactTextAfterToken != null)
651    {
652      elements.add(new ASN1OctetString(COMPACT_TEXT_AFTER_TOKEN_BER_TYPE,
653           compactTextAfterToken));
654    }
655
656    if ((preferredDeliveryMechanisms != null) &&
657        (! preferredDeliveryMechanisms.isEmpty()))
658    {
659      final ArrayList<ASN1Element> pdmElements =
660           new ArrayList<ASN1Element>(preferredDeliveryMechanisms.size());
661      for (final ObjectPair<String,String> p : preferredDeliveryMechanisms)
662      {
663        if (p.getSecond() == null)
664        {
665          pdmElements.add(new ASN1Sequence(
666               new ASN1OctetString(p.getFirst())));
667        }
668        else
669        {
670          pdmElements.add(new ASN1Sequence(
671               new ASN1OctetString(p.getFirst()),
672               new ASN1OctetString(p.getSecond())));
673        }
674      }
675
676      elements.add(new ASN1Sequence(PREFERRED_DELIVERY_MECHANISM_BER_TYPE,
677           pdmElements));
678    }
679
680    return new ASN1OctetString(new ASN1Sequence(elements).encode());
681  }
682
683
684
685  /**
686   * Retrieves the DN of the user to whom the password reset token should be
687   * delivered.
688   *
689   * @return  The DN of the user to whom the password reset token should be
690   *          delivered.
691   */
692  public String getUserDN()
693  {
694    return userDN;
695  }
696
697
698
699  /**
700   * Retrieves the text (if any) that should be used as the message subject for
701   * delivery mechanisms that can make use of a subject.
702   *
703   * @return  The text that should be used as the message subject for delivery
704   *          mechanisms that can make use of a subject, or {@code null} if no
705   *          subject should be used, or if the delivery mechanism should
706   *          attempt to automatically determine a subject.
707   */
708  public String getMessageSubject()
709  {
710    return messageSubject;
711  }
712
713
714
715  /**
716   * Retrieves the text (if any) that should appear before the single-use token
717   * in the message delivered to the user via a mechanism that does not impose
718   * significant constraints on message size.
719   *
720   * @return  The text that should appear before the single-use token in the
721   *          message delivered to the user via a mechanism that does not impose
722   *          significant constraints on message size, or {@code null} if there
723   *          should not be any text before the token.
724   */
725  public String getFullTextBeforeToken()
726  {
727    return fullTextBeforeToken;
728  }
729
730
731
732  /**
733   * Retrieves the text (if any) that should appear after the single-use token
734   * in the message delivered to the user via a mechanism that does not impose
735   * significant constraints on message size.
736   *
737   * @return  The text that should appear after the single-use token in the
738   *          message delivered to the user via a mechanism that does not impose
739   *          significant constraints on message size, or {@code null} if there
740   *          should not be any text after the token.
741   */
742  public String getFullTextAfterToken()
743  {
744    return fullTextAfterToken;
745  }
746
747
748
749  /**
750   * Retrieves the text (if any) that should appear before the single-use token
751   * in the message delivered to the user via a mechanism that imposes
752   * significant constraints on message size.
753   *
754   * @return  The text that should appear before the single-use token in the
755   *          message delivered to the user via a mechanism that imposes
756   *          significant constraints on message size, or {@code null} if there
757   *          should not be any text before the token.
758   */
759  public String getCompactTextBeforeToken()
760  {
761    return compactTextBeforeToken;
762  }
763
764
765
766  /**
767   * Retrieves the text (if any) that should appear after the single-use token
768   * in the message delivered to the user via a mechanism that imposes
769   * significant constraints on message size.
770   *
771   * @return  The text that should appear after the single-use token in the
772   *          message delivered to the user via a mechanism that imposes
773   *          significant constraints on message size, or {@code null} if there
774   *          should not be any text after the token.
775   */
776  public String getCompactTextAfterToken()
777  {
778    return compactTextAfterToken;
779  }
780
781
782
783  /**
784   * Retrieves an ordered list of the preferred delivery mechanisms that should
785   * be used to provide the password reset token to the user, optionally paired
786   * with a mechanism-specific recipient ID (e.g., a mobile phone number for SMS
787   * delivery, or an email address for email delivery) that can be used in the
788   * delivery.  If this list is non-empty, then the server will use the first
789   * mechanism in the list that the server supports and is available for the
790   * target user, and the server will only consider mechanisms in the provided
791   * list even if the server supports alternate mechanisms that are not
792   * included.  If this list is empty, then the server will attempt to select an
793   * appropriate delivery mechanism for the user.
794   *
795   * @return  An ordered list of the preferred delivery mechanisms for the
796   *          password reset token, or an empty list if none were provided.
797   */
798  public List<ObjectPair<String,String>> getPreferredDeliveryMechanisms()
799  {
800    return preferredDeliveryMechanisms;
801  }
802
803
804
805  /**
806   * {@inheritDoc}
807   */
808  @Override()
809  public DeliverPasswordResetTokenExtendedResult process(
810              final LDAPConnection connection, final int depth)
811         throws LDAPException
812  {
813    final ExtendedResult extendedResponse = super.process(connection, depth);
814    return new DeliverPasswordResetTokenExtendedResult(extendedResponse);
815  }
816
817
818
819  /**
820   * {@inheritDoc}.
821   */
822  @Override()
823  public DeliverPasswordResetTokenExtendedRequest duplicate()
824  {
825    return duplicate(getControls());
826  }
827
828
829
830  /**
831   * {@inheritDoc}.
832   */
833  @Override()
834  public DeliverPasswordResetTokenExtendedRequest duplicate(
835                                                       final Control[] controls)
836  {
837    final DeliverPasswordResetTokenExtendedRequest r =
838         new DeliverPasswordResetTokenExtendedRequest(userDN,
839              messageSubject, fullTextBeforeToken, fullTextAfterToken,
840              compactTextBeforeToken, compactTextAfterToken,
841              preferredDeliveryMechanisms, controls);
842    r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
843    return r;
844  }
845
846
847
848  /**
849   * {@inheritDoc}
850   */
851  @Override()
852  public String getExtendedRequestName()
853  {
854    return INFO_EXTENDED_REQUEST_NAME_DELIVER_PW_RESET_TOKEN.get();
855  }
856
857
858
859  /**
860   * {@inheritDoc}
861   */
862  @Override()
863  public void toString(final StringBuilder buffer)
864  {
865    buffer.append("DeliverPasswordResetTokenExtendedRequest(userDN='");
866    buffer.append(userDN);
867    buffer.append('\'');
868
869    if (messageSubject != null)
870    {
871      buffer.append(", messageSubject='");
872      buffer.append(messageSubject);
873      buffer.append('\'');
874    }
875
876    if (fullTextBeforeToken != null)
877    {
878      buffer.append(", fullTextBeforeToken='");
879      buffer.append(fullTextBeforeToken);
880      buffer.append('\'');
881    }
882
883    if (fullTextAfterToken != null)
884    {
885      buffer.append(", fullTextAfterToken='");
886      buffer.append(fullTextAfterToken);
887      buffer.append('\'');
888    }
889
890    if (compactTextBeforeToken != null)
891    {
892      buffer.append(", compactTextBeforeToken='");
893      buffer.append(compactTextBeforeToken);
894      buffer.append('\'');
895    }
896
897    if (compactTextAfterToken != null)
898    {
899      buffer.append(", compactTextAfterToken='");
900      buffer.append(compactTextAfterToken);
901      buffer.append('\'');
902    }
903
904    if (preferredDeliveryMechanisms != null)
905    {
906      buffer.append(", preferredDeliveryMechanisms={");
907
908      final Iterator<ObjectPair<String,String>> iterator =
909           preferredDeliveryMechanisms.iterator();
910      while (iterator.hasNext())
911      {
912        final ObjectPair<String,String> p = iterator.next();
913        buffer.append('\'');
914        buffer.append(p.getFirst());
915        if (p.getSecond() != null)
916        {
917          buffer.append('(');
918          buffer.append(p.getSecond());
919          buffer.append(')');
920        }
921        buffer.append('\'');
922        if (iterator.hasNext())
923        {
924          buffer.append(',');
925        }
926      }
927    }
928
929    final Control[] controls = getControls();
930    if (controls.length > 0)
931    {
932      buffer.append(", controls={");
933      for (int i=0; i < controls.length; i++)
934      {
935        if (i > 0)
936        {
937          buffer.append(", ");
938        }
939
940        buffer.append(controls[i]);
941      }
942      buffer.append('}');
943    }
944
945    buffer.append(')');
946  }
947}