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