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}