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