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