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