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