001/*
002 * Copyright 2013-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2013-2024 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) 2013-2024 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;
041
042import com.unboundid.asn1.ASN1Element;
043import com.unboundid.asn1.ASN1OctetString;
044import com.unboundid.asn1.ASN1Sequence;
045import com.unboundid.ldap.sdk.Control;
046import com.unboundid.ldap.sdk.ExtendedResult;
047import com.unboundid.ldap.sdk.LDAPException;
048import com.unboundid.ldap.sdk.ResultCode;
049import com.unboundid.util.Debug;
050import com.unboundid.util.NotMutable;
051import com.unboundid.util.NotNull;
052import com.unboundid.util.Nullable;
053import com.unboundid.util.StaticUtils;
054import com.unboundid.util.ThreadSafety;
055import com.unboundid.util.ThreadSafetyLevel;
056import com.unboundid.util.Validator;
057
058import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
059
060
061
062/**
063 * This class provides an implementation of an extended result that may be used
064 * to provide information about the result of processing for a deliver one-time
065 * password extended request.  If the one-time password was delivered
066 * successfully, then this result will include information about the mechanism
067 * through which that message was delivered.
068 * <BR>
069 * <BLOCKQUOTE>
070 *   <B>NOTE:</B>  This class, and other classes within the
071 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
072 *   supported for use against Ping Identity, UnboundID, and
073 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
074 *   for proprietary functionality or for external specifications that are not
075 *   considered stable or mature enough to be guaranteed to work in an
076 *   interoperable way with other types of LDAP servers.
077 * </BLOCKQUOTE>
078 * <BR>
079 * If the request was processed successfully, then the extended result will have
080 * an OID of 1.3.6.1.4.1.30221.2.6.25 and a value with the following encoding:
081 * <BR><BR>
082 * <PRE>
083 *   DeliverOTPResult ::= SEQUENCE {
084 *        deliveryMechanism     [0] OCTET STRING,
085 *        recipientDN           [1] LDAPDN,
086 *        recipientID           [2] OCTET STRING OPTIONAL,
087 *        message               [3] OCTET STRING OPTIONAL,
088 *        ... }
089 * </PRE>
090 *
091 * @see  com.unboundid.ldap.sdk.unboundidds.UnboundIDDeliveredOTPBindRequest
092 * @see  DeliverOneTimePasswordExtendedRequest
093 */
094@NotMutable()
095@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
096public final class DeliverOneTimePasswordExtendedResult
097       extends ExtendedResult
098{
099  /**
100   * The OID (1.3.6.1.4.1.30221.2.6.25) for the deliver one-time password
101   * extended result.
102   */
103  @NotNull public static final String DELIVER_OTP_RESULT_OID =
104       "1.3.6.1.4.1.30221.2.6.25";
105
106
107
108  /**
109   * The BER type for the delivery mechanism element.
110   */
111  private static final byte TYPE_MECH = (byte) 0x80;
112
113
114
115  /**
116   * The BER type for the recipient DN element.
117   */
118  private static final byte TYPE_RECIPIENT_DN = (byte) 0x81;
119
120
121
122  /**
123   * The BER type for the recipient ID element.
124   */
125  private static final byte TYPE_RECIPIENT_ID = (byte) 0x82;
126
127
128
129  /**
130   * The BER type for the delivery message element.
131   */
132  private static final byte TYPE_MESSAGE = (byte) 0x83;
133
134
135
136  /**
137   * The serial version UID for this serializable class.
138   */
139  private static final long serialVersionUID = 5077693879184160485L;
140
141
142
143  // The name of the mechanism by which the one-time password was delivered.
144  @Nullable private final String deliveryMechanism;
145
146  // An message providing additional information about the delivery of the
147  // one-time password.
148  @Nullable private final String deliveryMessage;
149
150  // An the DN of the user to whom the one-time password was sent.
151  @Nullable private final String recipientDN;
152
153  // An identifier for the recipient of the one-time password.
154  @Nullable private final String recipientID;
155
156
157
158  /**
159   * Creates a new deliver one-time password extended result from the provided
160   * generic extended result.
161   *
162   * @param  extendedResult  The generic extended result to be parsed as a
163   *                         deliver one-time password result.
164   *
165   * @throws LDAPException  If the provided extended result cannot be parsed as
166   *                         a deliver one-time password result.
167   */
168  public DeliverOneTimePasswordExtendedResult(
169              @NotNull final ExtendedResult extendedResult)
170       throws LDAPException
171  {
172    super(extendedResult);
173
174    final ASN1OctetString value = extendedResult.getValue();
175    if (value == null)
176    {
177      deliveryMechanism = null;
178      recipientDN = null;
179      recipientID = null;
180      deliveryMessage = null;
181      return;
182    }
183
184    String mech = null;
185    String dn = null;
186    String id = null;
187    String message = null;
188    try
189    {
190      for (final ASN1Element e :
191           ASN1Sequence.decodeAsSequence(value.getValue()).elements())
192      {
193        switch (e.getType())
194        {
195          case TYPE_MECH:
196            mech = ASN1OctetString.decodeAsOctetString(e).stringValue();
197            break;
198          case TYPE_RECIPIENT_DN:
199            dn = ASN1OctetString.decodeAsOctetString(e).stringValue();
200            break;
201          case TYPE_RECIPIENT_ID:
202            id = ASN1OctetString.decodeAsOctetString(e).stringValue();
203            break;
204          case TYPE_MESSAGE:
205            message = ASN1OctetString.decodeAsOctetString(e).stringValue();
206            break;
207          default:
208            throw new LDAPException(ResultCode.DECODING_ERROR,
209                 ERR_DELIVER_OTP_RES_UNEXPECTED_ELEMENT_TYPE.get(
210                      StaticUtils.toHex(e.getType())));
211        }
212      }
213    }
214    catch (final LDAPException le)
215    {
216      Debug.debugException(le);
217      throw le;
218    }
219    catch (final Exception e)
220    {
221      Debug.debugException(e);
222      throw new LDAPException(ResultCode.DECODING_ERROR,
223           ERR_DELIVER_OTP_RES_ERROR_PARSING_VALUE.get(
224                StaticUtils.getExceptionMessage(e)),
225           e);
226    }
227
228
229    if (mech == null)
230    {
231      throw new LDAPException(ResultCode.DECODING_ERROR,
232           ERR_DELIVER_OTP_RES_NO_MECH.get());
233    }
234    else
235    {
236      deliveryMechanism = mech;
237    }
238
239    if (dn == null)
240    {
241      throw new LDAPException(ResultCode.DECODING_ERROR,
242           ERR_DELIVER_OTP_RES_NO_RECIPIENT_DN.get());
243    }
244    else
245    {
246      recipientDN = dn;
247    }
248
249    recipientID = id;
250    deliveryMessage = message;
251  }
252
253
254
255  /**
256   * Creates a new deliver one-time password extended result with the provided
257   * information.
258   *
259   * @param  messageID          The message ID for the LDAP message that is
260   *                            associated with this LDAP result.
261   * @param  resultCode         The result code from the response.
262   * @param  diagnosticMessage  The diagnostic message from the response, if
263   *                            available.
264   * @param  matchedDN          The matched DN from the response, if available.
265   * @param  referralURLs       The set of referral URLs from the response, if
266   *                            available.
267   * @param  deliveryMechanism  The name of the mechanism by which the one-time
268   *                            password was delivered, if available.  This
269   *                            should be non-{@code null} for a success result.
270   * @param  recipientDN        The DN of the user to whom the one-time password
271   *                            was sent.  This should be non-{@code null} for a
272   *                            success result.
273   * @param  recipientID        An identifier for the user to whom the one-time
274   *                            password was delivered.  It may be {@code null}
275   *                            if no password was delivered or there is no
276   *                            appropriate identifier, but if a value is
277   *                            provided then it should appropriate for the
278   *                            delivery mechanism (e.g., the user's e-mail
279   *                            address if delivered via e-mail, a phone number
280   *                            if delivered via SMS or voice call, etc.).
281   * @param  deliveryMessage    A message providing additional information about
282   *                            the one-time password delivery, if available.
283   *                            If this is non-{@code null}, then the delivery
284   *                            mechanism must also be non-null.
285   * @param  responseControls   The set of controls from the response, if
286   *                            available.
287   */
288  public DeliverOneTimePasswordExtendedResult(final int messageID,
289              @NotNull final ResultCode resultCode,
290              @Nullable final String diagnosticMessage,
291              @Nullable final String matchedDN,
292              @Nullable final String[] referralURLs,
293              @Nullable final String deliveryMechanism,
294              @Nullable final String recipientDN,
295              @Nullable final String recipientID,
296              @Nullable final String deliveryMessage,
297              @Nullable final Control... responseControls)
298  {
299    super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
300         ((deliveryMechanism == null) ? null : DELIVER_OTP_RESULT_OID),
301         encodeValue(deliveryMechanism, recipientDN, recipientID,
302              deliveryMessage),
303         responseControls);
304
305    this.deliveryMechanism = deliveryMechanism;
306    this.recipientDN       = recipientDN;
307    this.recipientID       = recipientID;
308    this.deliveryMessage   = deliveryMessage;
309  }
310
311
312
313  /**
314   * Encodes the provided information into an ASN.1 octet string suitable for
315   * use as the value of this extended result.
316   *
317   * @param  deliveryMechanism  The name of the mechanism by which the one-time
318   *                            password was delivered, if available.  This
319   *                            should be non-{@code null} for a success result.
320   * @param  recipientDN        The DN of the user to whom the one-time password
321   *                            was sent.  This should be non-{@code null} for a
322   *                            success result.
323   * @param  recipientID        An identifier for the user to whom the one-time
324   *                            password was delivered.  It may be {@code null}
325   *                            if no password was delivered or there is no
326   *                            appropriate identifier, but if a value is
327   *                            provided then it should appropriate for the
328   *                            delivery mechanism (e.g., the user's e-mail
329   *                            address if delivered via e-mail, a phone number
330   *                            if delivered via SMS or voice call, etc.).
331   * @param  deliveryMessage    A message providing additional information about
332   *                            the one-time password delivery, if available.
333   *                            If this is non-{@code null}, then the delivery
334   *                            mechanism must also be non-null.
335   *
336   * @return  An ASN.1 octet string containing the encoded value, or
337   *          {@code null} if the extended result should not have a value.
338   */
339  @Nullable()
340  private static ASN1OctetString encodeValue(
341               @Nullable final String deliveryMechanism,
342               @Nullable final String recipientDN,
343               @Nullable final String recipientID,
344               @Nullable final String deliveryMessage)
345  {
346    if (deliveryMechanism == null)
347    {
348      Validator.ensureTrue((recipientID == null),
349           "The delivery mechanism must be non-null if the recipient ID " +
350                "is non-null.");
351      Validator.ensureTrue((deliveryMessage == null),
352           "The delivery mechanism must be non-null if the delivery message " +
353                "is non-null.");
354      return null;
355    }
356
357    Validator.ensureTrue((recipientDN != null),
358         "If a delivery mechanism is provided, then a recipient DN must also " +
359              "be provided.");
360
361    final ArrayList<ASN1Element> elements = new ArrayList<>(4);
362    elements.add(new ASN1OctetString(TYPE_MECH, deliveryMechanism));
363    elements.add(new ASN1OctetString(TYPE_RECIPIENT_DN, recipientDN));
364
365    if (recipientID != null)
366    {
367      elements.add(new ASN1OctetString(TYPE_RECIPIENT_ID, recipientID));
368    }
369
370    if (deliveryMessage != null)
371    {
372      elements.add(new ASN1OctetString(TYPE_MESSAGE, deliveryMessage));
373    }
374
375    return new ASN1OctetString(new ASN1Sequence(elements).encode());
376  }
377
378
379
380  /**
381   * Retrieves the name of the mechanism by which the one-time password was
382   * delivered to the end user, if available.
383   *
384   * @return  The name of the mechanism by which the one-time password was
385   *          delivered to the end user, or {@code null} if this is not
386   *          available.
387   */
388  @Nullable()
389  public String getDeliveryMechanism()
390  {
391    return deliveryMechanism;
392  }
393
394
395
396  /**
397   * Retrieves the DN of the user to whom the one-time password was delivered,
398   * if available.
399   *
400   * @return  The DN of the user to whom the one-time password was delivered, or
401   *          {@code null} if this is not available.
402   */
403  @Nullable()
404  public String getRecipientDN()
405  {
406    return recipientDN;
407  }
408
409
410
411  /**
412   * Retrieves an identifier for the user to whom the one-time password was
413   * delivered, if available.  If a recipient ID is provided, then it should be
414   * in a form appropriate to the delivery mechanism (e.g., an e-mail address
415   * if the password was delivered by e-mail, a phone number if it was delivered
416   * by SMS or a voice call, etc.).
417   *
418   * @return  An identifier for the user to whom the one-time password was
419   *          delivered, or {@code null} if this is not available.
420   */
421  @Nullable()
422  public String getRecipientID()
423  {
424    return recipientID;
425  }
426
427
428
429  /**
430   * Retrieves a message providing additional information about the one-time
431   * password delivery, if available.
432   *
433   * @return  A message providing additional information about the one-time
434   *          password delivery, or {@code null} if this is not available.
435   */
436  @Nullable()
437  public String getDeliveryMessage()
438  {
439    return deliveryMessage;
440  }
441
442
443
444  /**
445   * {@inheritDoc}
446   */
447  @Override()
448  @NotNull()
449  public String getExtendedResultName()
450  {
451    return INFO_DELIVER_OTP_RES_NAME.get();
452  }
453
454
455
456  /**
457   * Appends a string representation of this extended result to the provided
458   * buffer.
459   *
460   * @param  buffer  The buffer to which a string representation of this
461   *                 extended result will be appended.
462   */
463  @Override()
464  public void toString(@NotNull final StringBuilder buffer)
465  {
466    buffer.append("DeliverOneTimePasswordExtendedResult(resultCode=");
467    buffer.append(getResultCode());
468
469    final int messageID = getMessageID();
470    if (messageID >= 0)
471    {
472      buffer.append(", messageID=");
473      buffer.append(messageID);
474    }
475
476    if (deliveryMechanism != null)
477    {
478      buffer.append(", deliveryMechanism='");
479      buffer.append(deliveryMechanism);
480      buffer.append('\'');
481    }
482
483    if (recipientDN != null)
484    {
485      buffer.append(", recipientDN='");
486      buffer.append(recipientDN);
487      buffer.append('\'');
488    }
489
490    if (recipientID != null)
491    {
492      buffer.append(", recipientID='");
493      buffer.append(recipientID);
494      buffer.append('\'');
495    }
496
497    if (deliveryMessage != null)
498    {
499      buffer.append(", deliveryMessage='");
500      buffer.append(deliveryMessage);
501      buffer.append('\'');
502    }
503
504    final String diagnosticMessage = getDiagnosticMessage();
505    if (diagnosticMessage != null)
506    {
507      buffer.append(", diagnosticMessage='");
508      buffer.append(diagnosticMessage);
509      buffer.append('\'');
510    }
511
512    final String matchedDN = getMatchedDN();
513    if (matchedDN != null)
514    {
515      buffer.append(", matchedDN='");
516      buffer.append(matchedDN);
517      buffer.append('\'');
518    }
519
520    final String[] referralURLs = getReferralURLs();
521    if (referralURLs.length > 0)
522    {
523      buffer.append(", referralURLs={");
524      for (int i=0; i < referralURLs.length; i++)
525      {
526        if (i > 0)
527        {
528          buffer.append(", ");
529        }
530
531        buffer.append('\'');
532        buffer.append(referralURLs[i]);
533        buffer.append('\'');
534      }
535      buffer.append('}');
536    }
537
538    final Control[] responseControls = getResponseControls();
539    if (responseControls.length > 0)
540    {
541      buffer.append(", responseControls={");
542      for (int i=0; i < responseControls.length; i++)
543      {
544        if (i > 0)
545        {
546          buffer.append(", ");
547        }
548
549        buffer.append(responseControls[i]);
550      }
551      buffer.append('}');
552    }
553
554    buffer.append(')');
555  }
556}