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