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.ASN1Boolean;
031    import com.unboundid.asn1.ASN1Element;
032    import com.unboundid.asn1.ASN1Long;
033    import com.unboundid.asn1.ASN1OctetString;
034    import com.unboundid.asn1.ASN1Sequence;
035    import com.unboundid.ldap.sdk.Control;
036    import com.unboundid.ldap.sdk.ExtendedRequest;
037    import com.unboundid.ldap.sdk.ExtendedResult;
038    import com.unboundid.ldap.sdk.LDAPConnection;
039    import com.unboundid.ldap.sdk.LDAPException;
040    import com.unboundid.ldap.sdk.ResultCode;
041    import com.unboundid.util.Debug;
042    import com.unboundid.util.NotMutable;
043    import com.unboundid.util.ObjectPair;
044    import com.unboundid.util.StaticUtils;
045    import com.unboundid.util.ThreadSafety;
046    import com.unboundid.util.ThreadSafetyLevel;
047    import com.unboundid.util.Validator;
048    
049    import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
050    
051    
052    
053    /**
054     * <BLOCKQUOTE>
055     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
056     *   LDAP SDK for Java.  It is not available for use in applications that
057     *   include only the Standard Edition of the LDAP SDK, and is not supported for
058     *   use in conjunction with non-UnboundID products.
059     * </BLOCKQUOTE>
060     * This class provides an implementation of an extended request that can be used
061     * to trigger the delivery of a temporary single-use token to a specified user
062     * via some out-of-band mechanism.  It can be used for security purposes
063     * (e.g., as part of step-up authentication), for data validation purposes
064     * (e.g., to verify that a user can receive e-mail messages at a given address
065     * or SMS messages at a given phone number), or for other purposes in which it
066     * could be useful to deliver and consume a token through some out-of-band
067     * mechanism.
068     * <BR><BR>
069     * This extended request has an OID of "1.3.6.1.4.1.30221.2.6.49" and it must
070     * have a value with the following encoding:
071     * <PRE>
072     *   DeliverSingleUseTokenRequestValue ::= SEQUENCE {
073     *        userDN                         LDAPDN,
074     *        tokenID                        OCTET STRING,
075     *        validityDurationMillis         [0] INTEGER OPTIONAL,
076     *        messageSubject                 [1] OCTET STRING OPTIONAL,
077     *        fullTextBeforeToken            [2] OCTET STRING OPTIONAL,
078     *        fullTextAfterToken             [3] OCTET STRING OPTIONAL,
079     *        compactTextBeforeToken         [4] OCTET STRING OPTIONAL,
080     *        compactTextAfterToken          [5] OCTET STRING OPTIONAL,
081     *        preferredDeliveryMechanism     [6] SEQUENCE OF SEQUENCE {
082     *             mechanismName     OCTET STRING,
083     *             recipientID       OCTET STRING OPTIONAL },
084     *        deliverIfPasswordExpired       [7] BOOLEAN DEFAULT FALSE,
085     *        deliverIfAccountLocked         [8] BOOLEAN DEFAULT FALSE,
086     *        deliverIfAccountDisabled       [9] BOOLEAN DEFAULT FALSE,
087     *        deliverIfAccountExpired        [10] BOOLEAN DEFAULT FALSE,
088     *        ... }
089     * </PRE>
090     *
091     * @see  DeliverSingleUseTokenExtendedResult
092     * @see  ConsumeSingleUseTokenExtendedRequest
093     */
094    @NotMutable()
095    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
096    public final class DeliverSingleUseTokenExtendedRequest
097         extends ExtendedRequest
098    {
099      /**
100       * The OID (1.3.6.1.4.1.30221.2.6.49) for the deliver single-use token
101       * extended request.
102       */
103      public static final String DELIVER_SINGLE_USE_TOKEN_REQUEST_OID =
104           "1.3.6.1.4.1.30221.2.6.49";
105    
106    
107    
108      /**
109       * The BER type for the "validity duration millis" element of the value
110       * sequence.
111       */
112      private static final byte VALIDITY_DURATION_MILLIS_BER_TYPE = (byte) 0x80;
113    
114    
115    
116      /**
117       * The BER type for the "message subject" element of the value sequence.
118       */
119      private static final byte MESSAGE_SUBJECT_BER_TYPE = (byte) 0x81;
120    
121    
122    
123      /**
124       * The BER type for the "full text before token" element of the value
125       * sequence.
126       */
127      private static final byte FULL_TEXT_BEFORE_TOKEN_BER_TYPE = (byte) 0x82;
128    
129    
130    
131      /**
132       * The BER type for the "full text after token" element of the value
133       * sequence.
134       */
135      private static final byte FULL_TEXT_AFTER_TOKEN_BER_TYPE = (byte) 0x83;
136    
137    
138    
139      /**
140       * The BER type for the "compact text before token" element of the value
141       * sequence.
142       */
143      private static final byte COMPACT_TEXT_BEFORE_TOKEN_BER_TYPE = (byte) 0x84;
144    
145    
146    
147      /**
148       * The BER type for the "compact text after token" element of the value
149       * sequence.
150       */
151      private static final byte COMPACT_TEXT_AFTER_TOKEN_BER_TYPE = (byte) 0x85;
152    
153    
154    
155      /**
156       * The BER type for the "preferred delivery mechanism" element of the value
157       * sequence.
158       */
159      private static final byte PREFERRED_DELIVERY_MECHANISM_BER_TYPE = (byte) 0xA6;
160    
161    
162    
163      /**
164       * The BER type for the "deliver if password expired" element of the value
165       * sequence.
166       */
167      private static final byte DELIVER_IF_PASSWORD_EXPIRED_TYPE = (byte) 0x87;
168    
169    
170    
171      /**
172       * The BER type for the "deliver if account locked" element of the value
173       * sequence.
174       */
175      private static final byte DELIVER_IF_ACCOUNT_LOCKED_TYPE = (byte) 0x88;
176    
177    
178    
179      /**
180       * The BER type for the "deliver if account disabled" element of the value
181       * sequence.
182       */
183      private static final byte DELIVER_IF_ACCOUNT_DISABLED_TYPE = (byte) 0x89;
184    
185    
186    
187      /**
188       * The BER type for the "deliver if account expired" element of the value
189       * sequence.
190       */
191      private static final byte DELIVER_IF_ACCOUNT_EXPIRED_TYPE = (byte) 0x8A;
192    
193    
194    
195      /**
196       * The serial version UID for this serializable class.
197       */
198      private static final long serialVersionUID = -4158226639899928825L;
199    
200    
201    
202      // Indicates whether the server should attempt to deliver the token if the
203      // target user's account has been administratively disabled.
204      private final boolean deliverIfAccountDisabled;
205    
206      // Indicates whether the server should attempt to deliver the token if the
207      // target user's account has expired.
208      private final boolean deliverIfAccountExpired;
209    
210      // Indicates whether the server should attempt to deliver the token if the
211      // target user's account has been locked for some reason.
212      private final boolean deliverIfAccountLocked;
213    
214      // Indicates whether the server should attempt to deliver the token if the
215      // target user's password is expired.
216      private final boolean deliverIfPasswordExpired;
217    
218      // An optional list of the preferred delivery mechanisms that should be used.
219      private final List<ObjectPair<String,String>> preferredDeliveryMechanisms;
220    
221      // The maximum length of time, in milliseconds, that the token should be
222      // considered valid.
223      private final Long validityDurationMillis;
224    
225      // The text to include after the token in a compact message.
226      private final String compactTextAfterToken;
227    
228      // The text to include before the token in a compact message.
229      private final String compactTextBeforeToken;
230    
231      // The text to include after the token in a message without size constraints.
232      private final String fullTextAfterToken;
233    
234      // The text to include before the token in a message without size constraints.
235      private final String fullTextBeforeToken;
236    
237      // The text to use as the message subject.
238      private final String messageSubject;
239    
240      // The identifier that will be used when consuming this token.
241      private final String tokenID;
242    
243      // The DN of the user for whom the token should be generated and delivered.
244      private final String userDN;
245    
246    
247    
248      /**
249       * Creates a new deliver single-use token extended request with the provided
250       * information.
251       *
252       * @param  userDN                       The DN of the user for whom the token
253       *                                      should be generated and delivered.  It
254       *                                      must not be {@code null}.
255       * @param  tokenID                      An identifier for the token, which can
256       *                                      differentiate between separate uses of
257       *                                      this extended operation for different
258       *                                      purposes.  This token ID should be
259       *                                      provided in the request to consume the
260       *                                      token that has been delivered.  It
261       *                                      must not be {@code null}.
262       * @param  validityDurationMillis       The maximum length of time in
263       *                                      milliseconds that the generated token
264       *                                      should be considered valid.  It may be
265       *                                      {@code null} if the server should
266       *                                      determine the token validity duration.
267       *                                      If it is non-{@code null}, then the
268       *                                      value must be greater than zero.
269       * @param  messageSubject               The text (if any) that should be used
270       *                                      as the message subject if the delivery
271       *                                      mechanism accepts a subject.  This may
272       *                                      be {@code null} if no subject is
273       *                                      required or a subject should be
274       *                                      automatically generated.
275       * @param  fullTextBeforeToken          The text (if any) that should appear
276       *                                      before the generated single-use token
277       *                                      in the message delivered to the user
278       *                                      via a delivery mechanism that does not
279       *                                      impose significant constraints on
280       *                                      message size.  This may be
281       *                                      {@code null} if no text is required
282       *                                      before the token.
283       * @param  fullTextAfterToken           The text (if any) that should appear
284       *                                      after the generated single-use token
285       *                                      in the message delivered to the user
286       *                                      via a delivery mechanism that does not
287       *                                      impose significant constraints on
288       *                                      message size.  This may be
289       *                                      {@code null} if no text is required
290       *                                      after the token.
291       * @param  compactTextBeforeToken       The text (if any) that should appear
292       *                                      before the generated single-use token
293       *                                      in the message delivered to the user
294       *                                      via a delivery mechanism that imposes
295       *                                      significant constraints on message
296       *                                      size.  This may be {@code null} if no
297       *                                      text is required before the token.
298       * @param  compactTextAfterToken        The text (if any) that should appear
299       *                                      after the generated single-use token
300       *                                      in the message delivered to the user
301       *                                      via a delivery mechanism that imposes
302       *                                      significant constraints on message
303       *                                      size.  This may be {@code null} if no
304       *                                      text is required after the token.
305       * @param  preferredDeliveryMechanisms  An optional list of the preferred
306       *                                      delivery mechanisms that should be
307       *                                      used to convey the token to the target
308       *                                      user.  It may be {@code null} or empty
309       *                                      if the server should determine the
310       *                                      delivery mechanisms to attempt.  If
311       *                                      a list of preferred delivery
312       *                                      mechanisms is provided, the server
313       *                                      will only attempt to deliver the token
314       *                                      through these mechanisms, with
315       *                                      attempts made in the order specified
316       *                                      in this list.
317       * @param  deliverIfPasswordExpired     Indicates whether to generate and
318       *                                      deliver a token if the target user's
319       *                                      password is expired.
320       * @param  deliverIfAccountLocked       Indicates whether to generate and
321       *                                      deliver a token if the target user's
322       *                                      account is locked for some reason
323       *                                      (e.g., too many failed authentication
324       *                                      attempts, the account has been idle
325       *                                      for too long, the user failed to
326       *                                      change his/her password in a timely
327       *                                      manner after an administrative reset,
328       *                                      etc.).
329       * @param  deliverIfAccountDisabled     Indicates whether to generate and
330       *                                      deliver a token if the target user's
331       *                                      account has been disabled by an
332       *                                      administrator.
333       * @param  deliverIfAccountExpired      Indicates whether to generate and
334       *                                      deliver a token if the target user's
335       *                                      account has expired.
336       * @param  controls                     An optional set of controls to include
337       *                                      in the request.  It may be
338       *                                      {@code null} or empty if no controls
339       *                                      are required.
340       */
341      public DeliverSingleUseTokenExtendedRequest(final String userDN,
342                  final String tokenID, final Long validityDurationMillis,
343                  final String messageSubject, final String fullTextBeforeToken,
344                  final String fullTextAfterToken,
345                  final String compactTextBeforeToken,
346                  final String compactTextAfterToken,
347                  final List<ObjectPair<String,String>> preferredDeliveryMechanisms,
348                  final boolean deliverIfPasswordExpired,
349                  final boolean deliverIfAccountLocked,
350                  final boolean deliverIfAccountDisabled,
351                  final boolean deliverIfAccountExpired, final Control... controls)
352      {
353        super(DELIVER_SINGLE_USE_TOKEN_REQUEST_OID,
354             encodeValue(userDN, tokenID, validityDurationMillis, messageSubject,
355                  fullTextBeforeToken, fullTextAfterToken, compactTextBeforeToken,
356                  compactTextAfterToken, preferredDeliveryMechanisms,
357                  deliverIfPasswordExpired, deliverIfAccountLocked,
358                  deliverIfAccountDisabled, deliverIfAccountExpired),
359             controls);
360    
361        this.userDN                   = userDN;
362        this.tokenID                  = tokenID;
363        this.validityDurationMillis   = validityDurationMillis;
364        this.messageSubject           = messageSubject;
365        this.fullTextBeforeToken      = fullTextBeforeToken;
366        this.fullTextAfterToken       = fullTextAfterToken;
367        this.compactTextBeforeToken   = compactTextBeforeToken;
368        this.compactTextAfterToken    = compactTextAfterToken;
369        this.deliverIfPasswordExpired = deliverIfPasswordExpired;
370        this.deliverIfAccountLocked   = deliverIfAccountLocked;
371        this.deliverIfAccountDisabled = deliverIfAccountDisabled;
372        this.deliverIfAccountExpired  = deliverIfAccountExpired;
373    
374        if (preferredDeliveryMechanisms == null)
375        {
376          this.preferredDeliveryMechanisms = Collections.emptyList();
377        }
378        else
379        {
380          this.preferredDeliveryMechanisms = Collections.unmodifiableList(
381               new ArrayList<ObjectPair<String,String>>(
382                    preferredDeliveryMechanisms));
383        }
384      }
385    
386    
387    
388      /**
389       * Decodes the provided extended request as a deliver single-use token
390       * extended request.
391       *
392       * @param  request  The extended request to decode as a deliver single-use
393       *                  token extended request.
394       *
395       * @throws  LDAPException  If the provided extended request cannot be decoded
396       *                         as a deliver single-use token request.
397       */
398      public DeliverSingleUseTokenExtendedRequest(final ExtendedRequest request)
399             throws LDAPException
400      {
401        super(request);
402    
403        final ASN1OctetString value = request.getValue();
404        if (value == null)
405        {
406          throw new LDAPException(ResultCode.DECODING_ERROR,
407               ERR_DELIVER_SINGLE_USE_TOKEN_REQUEST_NO_VALUE.get());
408        }
409    
410        try
411        {
412          final ASN1Element[] elements =
413               ASN1Sequence.decodeAsSequence(value.getValue()).elements();
414          userDN = ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
415          tokenID = ASN1OctetString.decodeAsOctetString(elements[1]).stringValue();
416    
417          Long validityDuration = null;
418          String subject = null;
419          String fullBefore = null;
420          String fullAfter = null;
421          String compactBefore = null;
422          String compactAfter = null;
423          final ArrayList<ObjectPair<String,String>> pdmList =
424               new ArrayList<ObjectPair<String,String>>(10);
425          boolean ifPasswordExpired = false;
426          boolean ifAccountLocked = false;
427          boolean ifAccountDisabled = false;
428          boolean ifAccountExpired = false;
429          for (int i=2; i < elements.length; i++)
430          {
431            switch (elements[i].getType())
432            {
433              case VALIDITY_DURATION_MILLIS_BER_TYPE:
434                validityDuration = ASN1Long.decodeAsLong(elements[i]).longValue();
435                break;
436    
437              case MESSAGE_SUBJECT_BER_TYPE:
438                subject =
439                     ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
440                break;
441    
442              case FULL_TEXT_BEFORE_TOKEN_BER_TYPE:
443                fullBefore =
444                     ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
445                break;
446    
447              case FULL_TEXT_AFTER_TOKEN_BER_TYPE:
448                fullAfter =
449                     ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
450                break;
451    
452              case COMPACT_TEXT_BEFORE_TOKEN_BER_TYPE:
453                compactBefore =
454                     ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
455                break;
456    
457              case COMPACT_TEXT_AFTER_TOKEN_BER_TYPE:
458                compactAfter =
459                     ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
460                break;
461    
462              case PREFERRED_DELIVERY_MECHANISM_BER_TYPE:
463                for (final ASN1Element pdmElement :
464                     ASN1Sequence.decodeAsSequence(elements[i]).elements())
465                {
466                  final ASN1Element[] dmElements =
467                       ASN1Sequence.decodeAsSequence(pdmElement).elements();
468                  final String name = ASN1OctetString.decodeAsOctetString(
469                       dmElements[0]).stringValue();
470    
471                  final String recipientID;
472                  if (dmElements.length > 1)
473                  {
474                    recipientID = ASN1OctetString.decodeAsOctetString(
475                         dmElements[1]).stringValue();
476                  }
477                  else
478                  {
479                    recipientID = null;
480                  }
481                  pdmList.add(new ObjectPair<String,String>(name, recipientID));
482                }
483                break;
484    
485              case DELIVER_IF_PASSWORD_EXPIRED_TYPE:
486                ifPasswordExpired =
487                     ASN1Boolean.decodeAsBoolean(elements[i]).booleanValue();
488                break;
489    
490              case DELIVER_IF_ACCOUNT_LOCKED_TYPE:
491                ifAccountLocked =
492                     ASN1Boolean.decodeAsBoolean(elements[i]).booleanValue();
493                break;
494    
495              case DELIVER_IF_ACCOUNT_DISABLED_TYPE:
496                ifAccountDisabled =
497                     ASN1Boolean.decodeAsBoolean(elements[i]).booleanValue();
498                break;
499    
500              case DELIVER_IF_ACCOUNT_EXPIRED_TYPE:
501                ifAccountExpired =
502                     ASN1Boolean.decodeAsBoolean(elements[i]).booleanValue();
503                break;
504    
505              default:
506                throw new LDAPException(ResultCode.DECODING_ERROR,
507                     ERR_DELIVER_SINGLE_USE_TOKEN_REQUEST_UNKNOWN_ELEMENT.get(
508                          StaticUtils.toHex(elements[i].getType())));
509            }
510          }
511    
512          validityDurationMillis      = validityDuration;
513          messageSubject              = subject;
514          fullTextBeforeToken         = fullBefore;
515          fullTextAfterToken          = fullAfter;
516          compactTextBeforeToken      = compactBefore;
517          compactTextAfterToken       = compactAfter;
518          preferredDeliveryMechanisms = Collections.unmodifiableList(pdmList);
519          deliverIfPasswordExpired    = ifPasswordExpired;
520          deliverIfAccountLocked      = ifAccountLocked;
521          deliverIfAccountDisabled    = ifAccountDisabled;
522          deliverIfAccountExpired     = ifAccountExpired;
523        }
524        catch (final LDAPException le)
525        {
526          Debug.debugException(le);
527          throw le;
528        }
529        catch (final Exception e)
530        {
531          Debug.debugException(e);
532          throw new LDAPException(ResultCode.DECODING_ERROR,
533               ERR_DELIVER_SINGLE_USE_TOKEN_REQUEST_CANNOT_DECODE.get(
534                    StaticUtils.getExceptionMessage(e)),
535               e);
536        }
537      }
538    
539    
540    
541      /**
542       * Encodes the provided information into an ASN.1 octet string suitable for
543       * use as the value of the extended request.
544       *
545       * @param  userDN                       The DN of the user for whom the token
546       *                                      should be generated and delivered.  It
547       *                                      must not be {@code null}.
548       * @param  tokenID                      An identifier for the token, which can
549       *                                      differentiate between separate uses of
550       *                                      this extended operation for different
551       *                                      purposes.  This token ID should be
552       *                                      provided in the request to consume the
553       *                                      token that has been delivered.  It
554       *                                      must not be {@code null}.
555       * @param  validityDurationMillis       The maximum length of time in
556       *                                      milliseconds that the generated token
557       *                                      should be considered valid.  It may be
558       *                                      {@code null} if the server should
559       *                                      determine the token validity duration.
560       *                                      If it is non-{@code null}, then the
561       *                                      value must be greater than zero.
562       * @param  messageSubject               The text (if any) that should be used
563       *                                      as the message subject if the delivery
564       *                                      mechanism accepts a subject.  This may
565       *                                      be {@code null} if no subject is
566       *                                      required or a subject should be
567       *                                      automatically generated.
568       * @param  fullTextBeforeToken          The text (if any) that should appear
569       *                                      before the generated single-use token
570       *                                      in the message delivered to the user
571       *                                      via a delivery mechanism that does not
572       *                                      impose significant constraints on
573       *                                      message size.  This may be
574       *                                      {@code null} if no text is required
575       *                                      before the token.
576       * @param  fullTextAfterToken           The text (if any) that should appear
577       *                                      after the generated single-use token
578       *                                      in the message delivered to the user
579       *                                      via a delivery mechanism that does not
580       *                                      impose significant constraints on
581       *                                      message size.  This may be
582       *                                      {@code null} if no text is required
583       *                                      after the token.
584       * @param  compactTextBeforeToken       The text (if any) that should appear
585       *                                      before the generated single-use token
586       *                                      in the message delivered to the user
587       *                                      via a delivery mechanism that imposes
588       *                                      significant constraints on message
589       *                                      size.  This may be {@code null} if no
590       *                                      text is required before the token.
591       * @param  compactTextAfterToken        The text (if any) that should appear
592       *                                      after the generated single-use token
593       *                                      in the message delivered to the user
594       *                                      via a delivery mechanism that imposes
595       *                                      significant constraints on message
596       *                                      size.  This may be {@code null} if no
597       *                                      text is required after the token.
598       * @param  preferredDeliveryMechanisms  An optional list of the preferred
599       *                                      delivery mechanisms that should be
600       *                                      used to convey the token to the target
601       *                                      user.  It may be {@code null} or empty
602       *                                      if the server should determine the
603       *                                      delivery mechanisms to attempt.  If
604       *                                      a list of preferred delivery
605       *                                      mechanisms is provided, the server
606       *                                      will only attempt to deliver the token
607       *                                      through these mechanisms, with
608       *                                      attempts made in the order specified
609       *                                      in this list.
610       * @param  deliverIfPasswordExpired     Indicates whether to generate and
611       *                                      deliver a token if the target user's
612       *                                      password is expired.
613       * @param  deliverIfAccountLocked       Indicates whether to generate and
614       *                                      deliver a token if the target user's
615       *                                      account is locked for some reason
616       *                                      (e.g., too many failed authentication
617       *                                      attempts, the account has been idle
618       *                                      for too long, the user failed to
619       *                                      change his/her password in a timely
620       *                                      manner after an administrative reset,
621       *                                      etc.).
622       * @param  deliverIfAccountDisabled     Indicates whether to generate and
623       *                                      deliver a token if the target user's
624       *                                      account has been disabled by an
625       *                                      administrator.
626       * @param  deliverIfAccountExpired      Indicates whether to generate and
627       *                                      deliver a token if the target user's
628       *                                      account has expired.
629       *
630       * @return  An ASN.1 octet string containing the encoded value.
631       */
632      private static ASN1OctetString encodeValue(final String userDN,
633           final String tokenID, final Long validityDurationMillis,
634           final String messageSubject, final String fullTextBeforeToken,
635           final String fullTextAfterToken, final String compactTextBeforeToken,
636           final String compactTextAfterToken,
637           final List<ObjectPair<String,String>> preferredDeliveryMechanisms,
638           final boolean deliverIfPasswordExpired,
639           final boolean deliverIfAccountLocked,
640           final boolean deliverIfAccountDisabled,
641           final boolean deliverIfAccountExpired)
642      {
643        Validator.ensureNotNull(userDN);
644        Validator.ensureNotNull(tokenID);
645    
646        if (validityDurationMillis != null)
647        {
648          Validator.ensureTrue(validityDurationMillis > 0L);
649        }
650    
651    
652        final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(13);
653        elements.add(new ASN1OctetString(userDN));
654        elements.add(new ASN1OctetString(tokenID));
655    
656        if (validityDurationMillis != null)
657        {
658          elements.add(new ASN1Long(VALIDITY_DURATION_MILLIS_BER_TYPE,
659               validityDurationMillis));
660        }
661    
662        if (messageSubject != null)
663        {
664          elements.add(new ASN1OctetString(MESSAGE_SUBJECT_BER_TYPE,
665               messageSubject));
666        }
667    
668        if (fullTextBeforeToken != null)
669        {
670          elements.add(new ASN1OctetString(FULL_TEXT_BEFORE_TOKEN_BER_TYPE,
671               fullTextBeforeToken));
672        }
673    
674        if (fullTextAfterToken != null)
675        {
676          elements.add(new ASN1OctetString(FULL_TEXT_AFTER_TOKEN_BER_TYPE,
677               fullTextAfterToken));
678        }
679    
680        if (compactTextBeforeToken != null)
681        {
682          elements.add(new ASN1OctetString(COMPACT_TEXT_BEFORE_TOKEN_BER_TYPE,
683               compactTextBeforeToken));
684        }
685    
686        if (compactTextAfterToken != null)
687        {
688          elements.add(new ASN1OctetString(COMPACT_TEXT_AFTER_TOKEN_BER_TYPE,
689               compactTextAfterToken));
690        }
691    
692        if ((preferredDeliveryMechanisms != null) &&
693            (! preferredDeliveryMechanisms.isEmpty()))
694        {
695          final ArrayList<ASN1Element> pdmElements =
696               new ArrayList<ASN1Element>(preferredDeliveryMechanisms.size());
697          for (final ObjectPair<String,String> p : preferredDeliveryMechanisms)
698          {
699            final ArrayList<ASN1Element> l = new ArrayList<ASN1Element>(2);
700            l.add(new ASN1OctetString(p.getFirst()));
701            if (p.getSecond() != null)
702            {
703              l.add(new ASN1OctetString(p.getSecond()));
704            }
705            pdmElements.add(new ASN1Sequence(l));
706          }
707          elements.add(new ASN1Sequence(PREFERRED_DELIVERY_MECHANISM_BER_TYPE,
708               pdmElements));
709        }
710    
711        if (deliverIfPasswordExpired)
712        {
713          elements.add(new ASN1Boolean(DELIVER_IF_PASSWORD_EXPIRED_TYPE, true));
714        }
715    
716        if (deliverIfAccountLocked)
717        {
718          elements.add(new ASN1Boolean(DELIVER_IF_ACCOUNT_LOCKED_TYPE, true));
719        }
720    
721        if (deliverIfAccountDisabled)
722        {
723          elements.add(new ASN1Boolean(DELIVER_IF_ACCOUNT_DISABLED_TYPE, true));
724        }
725    
726        if (deliverIfAccountExpired)
727        {
728          elements.add(new ASN1Boolean(DELIVER_IF_ACCOUNT_EXPIRED_TYPE, true));
729        }
730    
731        return new ASN1OctetString(new ASN1Sequence(elements).encode());
732      }
733    
734    
735    
736      /**
737       * Retrieves the DN of the user for whom the token should be generated and
738       * delivered.
739       *
740       * @return  The DN of the user for whom the token should be generated and
741       *          delivered.
742       */
743      public String getUserDN()
744      {
745        return userDN;
746      }
747    
748    
749    
750      /**
751       * Retrieves an identifier for the token, which can differentiate between
752       * separate uses of this extended operation for different purposes, and should
753       * be provided when consuming the token via the
754       * {@link ConsumeSingleUseTokenExtendedRequest}.
755       *
756       * @return  An identifier for the token.
757       */
758      public String getTokenID()
759      {
760        return tokenID;
761      }
762    
763    
764    
765      /**
766       * Retrieves the maximum length of time in milliseconds that the generated
767       * token should be considered valid, if defined.  An attempt to consume the
768       * token after this length of time has elapsed will fail.
769       *
770       * @return  The maximum length of time in milliseconds that the generated
771       *          token should be considered valid, or {@code null} if the client
772       *          did not specify a value and the token validity duration will be
773       *          determined by the server.
774       */
775      public Long getValidityDurationMillis()
776      {
777        return validityDurationMillis;
778      }
779    
780    
781    
782      /**
783       * Retrieves the text (if any) that should be used as the message subject for
784       * delivery mechanisms that can make use of a subject.
785       *
786       * @return  The text that should be used as the message subject for delivery
787       *          mechanisms that can make use of a subject, or {@code null} if no
788       *          subject should be used, or if the delivery mechanism should
789       *          attempt to automatically determine a subject.
790       */
791      public String getMessageSubject()
792      {
793        return messageSubject;
794      }
795    
796    
797    
798      /**
799       * Retrieves the text (if any) that should appear before the single-use token
800       * in the message delivered to the user via a mechanism that does not impose
801       * significant constraints on message size.
802       *
803       * @return  The text that should appear before the single-use token in the
804       *          message delivered to the user via a mechanism that does not impose
805       *          significant constraints on message size, or {@code null} if there
806       *          should not be any text before the token.
807       */
808      public String getFullTextBeforeToken()
809      {
810        return fullTextBeforeToken;
811      }
812    
813    
814    
815      /**
816       * Retrieves the text (if any) that should appear after the single-use token
817       * in the message delivered to the user via a mechanism that does not impose
818       * significant constraints on message size.
819       *
820       * @return  The text that should appear after the single-use token in the
821       *          message delivered to the user via a mechanism that does not impose
822       *          significant constraints on message size, or {@code null} if there
823       *          should not be any text after the token.
824       */
825      public String getFullTextAfterToken()
826      {
827        return fullTextAfterToken;
828      }
829    
830    
831    
832      /**
833       * Retrieves the text (if any) that should appear before the single-use token
834       * in the message delivered to the user via a mechanism that imposes
835       * significant constraints on message size.
836       *
837       * @return  The text that should appear before the single-use token in the
838       *          message delivered to the user via a mechanism that imposes
839       *          significant constraints on message size, or {@code null} if there
840       *          should not be any text before the token.
841       */
842      public String getCompactTextBeforeToken()
843      {
844        return compactTextBeforeToken;
845      }
846    
847    
848    
849      /**
850       * Retrieves the text (if any) that should appear after the single-use token
851       * in the message delivered to the user via a mechanism that imposes
852       * significant constraints on message size.
853       *
854       * @return  The text that should appear after the single-use token in the
855       *          message delivered to the user via a mechanism that imposes
856       *          significant constraints on message size, or {@code null} if there
857       *          should not be any text after the token.
858       */
859      public String getCompactTextAfterToken()
860      {
861        return compactTextAfterToken;
862      }
863    
864    
865    
866      /**
867       * Retrieves a list of the preferred delivery mechanisms that should be used
868       * to provide the generated token to the target user.  If the returned list is
869       * empty, then the server will attempt to determine which mechanism(s) to use
870       * and in which order to try them.  If this list is not empty, then the server
871       * will only attempt the specified mechanisms and in the order in which they
872       * are listed.
873       *
874       * @return  A list of the preferred delivery mechanisms that should be used to
875       *          provide the generated token to the target user, or an empty list
876       *          if the server should determine the delivery mechanisms to attempt.
877       */
878      public List<ObjectPair<String,String>> getPreferredDeliveryMechanisms()
879      {
880        return preferredDeliveryMechanisms;
881      }
882    
883    
884    
885      /**
886       * Indicates whether to attempt to generate and deliver a token if the
887       * target user's password is expired.
888       *
889       * @return  {@code true} if the server should attempt to deliver a token to a
890       *          user with an expired password, or {@code false} if not.
891       */
892      public boolean deliverIfPasswordExpired()
893      {
894        return deliverIfPasswordExpired;
895      }
896    
897    
898    
899      /**
900       * Indicates whether to attempt to generate and deliver a token if the
901       * target user's account is locked for some reason (e.g., because there have
902       * been too many failed authentication attempts, because the account has been
903       * idle for too long, or because the password was not changed soon enough
904       * after an administrative reset).
905       *
906       * @return  {@code true} if the server should attempt to deliver a token to a
907       *          user with a locked account, or {@code false} if not.
908       */
909      public boolean deliverIfAccountLocked()
910      {
911        return deliverIfAccountLocked;
912      }
913    
914    
915    
916      /**
917       * Indicates whether to attempt to generate and deliver a token if the
918       * target user's account has been disabled by an administrator.
919       *
920       * @return  {@code true} if the server should attempt to deliver a token to a
921       *          user with a disabled account, or {@code false} if not.
922       */
923      public boolean deliverIfAccountDisabled()
924      {
925        return deliverIfAccountDisabled;
926      }
927    
928    
929    
930      /**
931       * Indicates whether to attempt to generate and deliver a token if the
932       * target user's account has expired.
933       *
934       * @return  {@code true} if the server should attempt to deliver a token to a
935       *          user with an expired account, or {@code false} if not.
936       */
937      public boolean deliverIfAccountExpired()
938      {
939        return deliverIfAccountExpired;
940      }
941    
942    
943    
944      /**
945       * {@inheritDoc}
946       */
947      @Override()
948      public DeliverSingleUseTokenExtendedResult process(
949                  final LDAPConnection connection, final int depth)
950             throws LDAPException
951      {
952        final ExtendedResult extendedResponse = super.process(connection, depth);
953        return new DeliverSingleUseTokenExtendedResult(extendedResponse);
954      }
955    
956    
957    
958      /**
959       * {@inheritDoc}.
960       */
961      @Override()
962      public DeliverSingleUseTokenExtendedRequest duplicate()
963      {
964        return duplicate(getControls());
965      }
966    
967    
968    
969      /**
970       * {@inheritDoc}.
971       */
972      @Override()
973      public DeliverSingleUseTokenExtendedRequest duplicate(
974                                                       final Control[] controls)
975      {
976        final DeliverSingleUseTokenExtendedRequest r =
977             new DeliverSingleUseTokenExtendedRequest(userDN, tokenID,
978                  validityDurationMillis, messageSubject, fullTextBeforeToken,
979                  fullTextAfterToken, compactTextBeforeToken, compactTextAfterToken,
980                  preferredDeliveryMechanisms, deliverIfPasswordExpired,
981                  deliverIfAccountLocked, deliverIfAccountDisabled,
982                  deliverIfAccountExpired, controls);
983        r.setResponseTimeoutMillis(getResponseTimeoutMillis(null));
984        return r;
985      }
986    
987    
988    
989      /**
990       * {@inheritDoc}
991       */
992      @Override()
993      public String getExtendedRequestName()
994      {
995        return INFO_EXTENDED_REQUEST_NAME_DELIVER_SINGLE_USE_TOKEN.get();
996      }
997    
998    
999    
1000      /**
1001       * {@inheritDoc}
1002       */
1003      @Override()
1004      public void toString(final StringBuilder buffer)
1005      {
1006        buffer.append("DeliverSingleUseTokenExtendedRequest(userDN='");
1007        buffer.append(userDN);
1008        buffer.append("', tokenID='");
1009        buffer.append(tokenID);
1010        buffer.append('\'');
1011    
1012        if (validityDurationMillis != null)
1013        {
1014          buffer.append(", validityDurationMillis=");
1015          buffer.append(validityDurationMillis);
1016        }
1017    
1018        if (messageSubject != null)
1019        {
1020          buffer.append(", messageSubject='");
1021          buffer.append(messageSubject);
1022          buffer.append('\'');
1023        }
1024    
1025        if (fullTextBeforeToken != null)
1026        {
1027          buffer.append(", fullTextBeforeToken='");
1028          buffer.append(fullTextBeforeToken);
1029          buffer.append('\'');
1030        }
1031    
1032        if (fullTextAfterToken != null)
1033        {
1034          buffer.append(", fullTextAfterToken='");
1035          buffer.append(fullTextAfterToken);
1036          buffer.append('\'');
1037        }
1038    
1039        if (compactTextBeforeToken != null)
1040        {
1041          buffer.append(", compactTextBeforeToken='");
1042          buffer.append(compactTextBeforeToken);
1043          buffer.append('\'');
1044        }
1045    
1046        if (compactTextAfterToken != null)
1047        {
1048          buffer.append(", compactTextAfterToken='");
1049          buffer.append(compactTextAfterToken);
1050          buffer.append('\'');
1051        }
1052    
1053        if (preferredDeliveryMechanisms != null)
1054        {
1055          buffer.append(", preferredDeliveryMechanisms={");
1056    
1057          final Iterator<ObjectPair<String,String>> iterator =
1058               preferredDeliveryMechanisms.iterator();
1059          while (iterator.hasNext())
1060          {
1061            final ObjectPair<String,String> p = iterator.next();
1062            buffer.append('\'');
1063            buffer.append(p.getFirst());
1064            if (p.getSecond() != null)
1065            {
1066              buffer.append('(');
1067              buffer.append(p.getSecond());
1068              buffer.append(')');
1069            }
1070            buffer.append('\'');
1071            if (iterator.hasNext())
1072            {
1073              buffer.append(',');
1074            }
1075          }
1076        }
1077    
1078        buffer.append(", deliverIfPasswordExpired=");
1079        buffer.append(deliverIfPasswordExpired);
1080        buffer.append(", deliverIfAccountLocked=");
1081        buffer.append(deliverIfAccountLocked);
1082        buffer.append(", deliverIfAccountDisabled=");
1083        buffer.append(deliverIfAccountDisabled);
1084        buffer.append(", deliverIfAccountExpired=");
1085        buffer.append(deliverIfAccountExpired);
1086    
1087        final Control[] controls = getControls();
1088        if (controls.length > 0)
1089        {
1090          buffer.append(", controls={");
1091          for (int i=0; i < controls.length; i++)
1092          {
1093            if (i > 0)
1094            {
1095              buffer.append(", ");
1096            }
1097    
1098            buffer.append(controls[i]);
1099          }
1100          buffer.append('}');
1101        }
1102    
1103        buffer.append(')');
1104      }
1105    }