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