001/*
002 * Copyright 2015-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2015-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) 2015-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
065 * single-use token extended request.  If the token was delivered successfully,
066 * then this result will include information about the mechanism through which
067 * the token 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.50 and a value with the following encoding:
081 * <BR><BR>
082 * <PRE>
083 *   DeliverSingleUseTokenResult ::= SEQUENCE {
084 *        deliveryMechanism     OCTET STRING,
085 *        recipientID           [0] OCTET STRING OPTIONAL,
086 *        message               [1] OCTET STRING OPTIONAL,
087 *        ... }
088 * </PRE>
089 *
090 * @see  DeliverSingleUseTokenExtendedRequest
091 * @see  ConsumeSingleUseTokenExtendedRequest
092 */
093@NotMutable()
094@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
095public final class DeliverSingleUseTokenExtendedResult
096       extends ExtendedResult
097{
098  /**
099   * The OID (1.3.6.1.4.1.30221.2.6.50) for the deliver single-use token
100   * extended result.
101   */
102  @NotNull public static final String DELIVER_SINGLE_USE_TOKEN_RESULT_OID =
103       "1.3.6.1.4.1.30221.2.6.50";
104
105
106
107  /**
108   * The BER type for the recipient ID element of the value sequence.
109   */
110  private static final byte RECIPIENT_ID_BER_TYPE = (byte) 0x80;
111
112
113
114  /**
115   * The BER type for the message element of the value sequence.
116   */
117  private static final byte DELIVERY_MESSAGE_BER_TYPE = (byte) 0x81;
118
119
120
121  /**
122   * The serial version UID for this serializable class.
123   */
124  private static final long serialVersionUID = 8874679715973086041L;
125
126
127
128  // The name of the mechanism by which the single-use token was delivered.
129  @Nullable private final String deliveryMechanism;
130
131  // An message providing additional information about the delivery of the
132  // single-use token.
133  @Nullable private final String deliveryMessage;
134
135  // An identifier for the recipient of the single-use token.
136  @Nullable private final String recipientID;
137
138
139
140  /**
141   * Creates a new deliver single-use token extended result with the provided
142   * information.
143   *
144   * @param  messageID          The message ID for the LDAP message that is
145   *                            associated with this LDAP result.
146   * @param  resultCode         The result code from the response.  It must not
147   *                            be {@code null}.
148   * @param  diagnosticMessage  The diagnostic message from the response, if
149   *                            available.
150   * @param  matchedDN          The matched DN from the response, if available.
151   * @param  referralURLs       The set of referral URLs from the response, if
152   *                            available.
153   * @param  deliveryMechanism  The name of the mechanism by which the token was
154   *                            delivered, if available.  This should be
155   *                            non-{@code null} for a success result.
156   * @param  recipientID        An identifier for the user to whom the token was
157   *                            delivered.  It may be {@code null} if no token
158   *                            was delivered or there is no appropriate
159   *                            identifier, but if a value is provided then it
160   *                            should appropriate for the delivery mechanism
161   *                            (e.g., the user's e-mail address if delivered
162   *                            via e-mail, a phone number if delivered via SMS
163   *                            or voice call, etc.).
164   * @param  deliveryMessage    An optional message providing additional
165   *                            information about the token delivery, if
166   *                            available.  If this is non-{@code null}, then
167   *                            the delivery mechanism must also be
168   *                            non-{@code null}.
169   * @param  responseControls   The set of controls for the response, if
170   *                            available.
171   */
172  public DeliverSingleUseTokenExtendedResult(final int messageID,
173              @NotNull final ResultCode resultCode,
174              @Nullable final String diagnosticMessage,
175              @Nullable final String matchedDN,
176              @Nullable final String[] referralURLs,
177              @Nullable final String deliveryMechanism,
178              @Nullable final String recipientID,
179              @Nullable final String deliveryMessage,
180              @Nullable final Control... responseControls)
181  {
182    super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
183         ((deliveryMechanism == null)
184              ? null : DELIVER_SINGLE_USE_TOKEN_RESULT_OID),
185         encodeValue(deliveryMechanism, recipientID, deliveryMessage),
186         responseControls);
187
188    this.deliveryMechanism = deliveryMechanism;
189    this.recipientID       = recipientID;
190    this.deliveryMessage   = deliveryMessage;
191  }
192
193
194
195  /**
196   * Creates a new deliver single-use token result from the provided generic
197   * extended result.
198   *
199   * @param  result  The generic extended result to be parsed as a deliver
200   *                 single-use token result.
201   *
202   * @throws LDAPException  If the provided extended result cannot be parsed as
203   *                         a deliver single-use token result.
204   */
205  public DeliverSingleUseTokenExtendedResult(
206              @NotNull final ExtendedResult result)
207         throws LDAPException
208  {
209    super(result);
210
211    final ASN1OctetString value = result.getValue();
212    if (value == null)
213    {
214      deliveryMechanism = null;
215      recipientID       = null;
216      deliveryMessage   = null;
217      return;
218    }
219
220    try
221    {
222      final ASN1Element[] elements =
223           ASN1Sequence.decodeAsSequence(value.getValue()).elements();
224      deliveryMechanism =
225           ASN1OctetString.decodeAsOctetString(elements[0]).stringValue();
226
227      String id = null;
228      String msg = null;
229      for (int i=1; i < elements.length; i++)
230      {
231        switch (elements[i].getType())
232        {
233          case RECIPIENT_ID_BER_TYPE:
234            id = ASN1OctetString.decodeAsOctetString(elements[i]).stringValue();
235            break;
236
237          case DELIVERY_MESSAGE_BER_TYPE:
238            msg = ASN1OctetString.decodeAsOctetString(
239                 elements[i]).stringValue();
240            break;
241
242          default:
243            throw new LDAPException(ResultCode.DECODING_ERROR,
244                 ERR_DELIVER_SINGLE_USE_TOKEN_RESULT_UNEXPECTED_TYPE.get(
245                      StaticUtils.toHex(elements[i].getType())));
246        }
247      }
248
249      recipientID = id;
250      deliveryMessage = msg;
251    }
252    catch (final LDAPException le)
253    {
254      Debug.debugException(le);
255      throw le;
256    }
257    catch (final Exception e)
258    {
259      Debug.debugException(e);
260      throw new LDAPException(ResultCode.DECODING_ERROR,
261           ERR_DELIVER_SINGLE_USE_TOKEN_RESULT_ERROR_DECODING_VALUE.get(
262                StaticUtils.getExceptionMessage(e)),
263           e);
264    }
265  }
266
267
268
269  /**
270   * Encodes the provided information into an ASN.1 octet string suitable for
271   * use as the value of this extended result.
272   *
273   * @param  deliveryMechanism  The name of the mechanism by which the token was
274   *                            delivered, if available.  This should be
275   *                            non-{@code null} for a success result.
276   * @param  recipientID        An identifier for the user to whom the token was
277   *                            delivered.  It may be {@code null} if no token
278   *                            was delivered or there is no appropriate
279   *                            identifier, but if a value is provided then it
280   *                            should appropriate for the delivery mechanism
281   *                            (e.g., the user's e-mail address if delivered
282   *                            via e-mail, a phone number if delivered via SMS
283   *                            or voice call, etc.).
284   * @param  deliveryMessage    An optional message providing additional
285   *                            information about the token delivery, if
286   *                            available.  If this is non-{@code null}, then
287   *                            the delivery mechanism must also be
288   *                            non-{@code null}.
289   *
290   * @return  An ASN.1 octet string containing the encoded value, or
291   *          {@code null} if the extended result should not have a value.
292   */
293  @Nullable()
294  private static ASN1OctetString encodeValue(
295               @Nullable final String deliveryMechanism,
296               @Nullable final String recipientID,
297               @Nullable final String deliveryMessage)
298  {
299    if (deliveryMechanism == null)
300    {
301      Validator.ensureTrue((recipientID == null),
302           "The delivery mechanism must be non-null if the recipient ID " +
303                "is non-null.");
304      Validator.ensureTrue((deliveryMessage == null),
305           "The delivery mechanism must be non-null if the delivery message " +
306                "is non-null.");
307      return null;
308    }
309
310    final ArrayList<ASN1Element> elements = new ArrayList<>(3);
311    elements.add(new ASN1OctetString(deliveryMechanism));
312
313    if (recipientID != null)
314    {
315      elements.add(new ASN1OctetString(RECIPIENT_ID_BER_TYPE, recipientID));
316    }
317
318    if (deliveryMessage != null)
319    {
320      elements.add(new ASN1OctetString(DELIVERY_MESSAGE_BER_TYPE,
321           deliveryMessage));
322    }
323
324    return new ASN1OctetString(new ASN1Sequence(elements).encode());
325  }
326
327
328
329  /**
330   * Retrieves the name of the mechanism by which the single-use token was
331   * delivered to the user, if available.
332   *
333   * @return  The name of the mechanism by which the single-use token was
334   *          delivered to the user, or {@code null} if this is not available.
335   */
336  @Nullable()
337  public String getDeliveryMechanism()
338  {
339    return deliveryMechanism;
340  }
341
342
343
344  /**
345   * Retrieves an identifier for the user to whom the single-use token was
346   * delivered, if available.  If a recipient ID is provided, then it should be
347   * in a form appropriate to the delivery mechanism (e.g., an e-mail address
348   * if the token was delivered by e-mail, a phone number if it was delivered
349   * by SMS or a voice call, etc.).
350   *
351   * @return  An identifier for the user to whom the single-use token was
352   *          delivered, or {@code null} if this is not available.
353   */
354  @Nullable()
355  public String getRecipientID()
356  {
357    return recipientID;
358  }
359
360
361
362  /**
363   * Retrieves a message providing additional information about the single-use
364   * token delivery, if available.
365   *
366   * @return  A message providing additional information about the single-use
367   *          token delivery, or {@code null} if this is not available.
368   */
369  @Nullable()
370  public String getDeliveryMessage()
371  {
372    return deliveryMessage;
373  }
374
375
376
377  /**
378   * {@inheritDoc}
379   */
380  @Override()
381  @NotNull()
382  public String getExtendedResultName()
383  {
384    return INFO_EXTENDED_RESULT_NAME_DELIVER_SINGLE_USE_TOKEN.get();
385  }
386
387
388
389  /**
390   * Appends a string representation of this extended result to the provided
391   * buffer.
392   *
393   * @param  buffer  The buffer to which a string representation of this
394   *                 extended result will be appended.
395   */
396  @Override()
397  public void toString(@NotNull final StringBuilder buffer)
398  {
399    buffer.append("DeliverSingleUseTokenExtendedResult(resultCode=");
400    buffer.append(getResultCode());
401
402    final int messageID = getMessageID();
403    if (messageID >= 0)
404    {
405      buffer.append(", messageID=");
406      buffer.append(messageID);
407    }
408
409    if (deliveryMechanism != null)
410    {
411      buffer.append(", deliveryMechanism='");
412      buffer.append(deliveryMechanism);
413      buffer.append('\'');
414    }
415
416    if (recipientID != null)
417    {
418      buffer.append(", recipientID='");
419      buffer.append(recipientID);
420      buffer.append('\'');
421    }
422
423    if (deliveryMessage != null)
424    {
425      buffer.append(", deliveryMessage='");
426      buffer.append(deliveryMessage);
427      buffer.append('\'');
428    }
429
430    final String diagnosticMessage = getDiagnosticMessage();
431    if (diagnosticMessage != null)
432    {
433      buffer.append(", diagnosticMessage='");
434      buffer.append(diagnosticMessage);
435      buffer.append('\'');
436    }
437
438    final String matchedDN = getMatchedDN();
439    if (matchedDN != null)
440    {
441      buffer.append(", matchedDN='");
442      buffer.append(matchedDN);
443      buffer.append('\'');
444    }
445
446    final String[] referralURLs = getReferralURLs();
447    if (referralURLs.length > 0)
448    {
449      buffer.append(", referralURLs={");
450      for (int i=0; i < referralURLs.length; i++)
451      {
452        if (i > 0)
453        {
454          buffer.append(", ");
455        }
456
457        buffer.append('\'');
458        buffer.append(referralURLs[i]);
459        buffer.append('\'');
460      }
461      buffer.append('}');
462    }
463
464    final Control[] responseControls = getResponseControls();
465    if (responseControls.length > 0)
466    {
467      buffer.append(", responseControls={");
468      for (int i=0; i < responseControls.length; i++)
469      {
470        if (i > 0)
471        {
472          buffer.append(", ");
473        }
474
475        buffer.append(responseControls[i]);
476      }
477      buffer.append('}');
478    }
479
480    buffer.append(')');
481  }
482}