001/* 002 * Copyright 2016-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2016-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) 2016-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; 037 038 039 040import java.util.ArrayList; 041import java.util.List; 042 043import com.unboundid.asn1.ASN1Element; 044import com.unboundid.asn1.ASN1OctetString; 045import com.unboundid.asn1.ASN1Sequence; 046import com.unboundid.ldap.sdk.BindResult; 047import com.unboundid.ldap.sdk.Control; 048import com.unboundid.ldap.sdk.InternalSDKHelper; 049import com.unboundid.ldap.sdk.LDAPConnection; 050import com.unboundid.ldap.sdk.LDAPException; 051import com.unboundid.ldap.sdk.ResultCode; 052import com.unboundid.ldap.sdk.SASLBindRequest; 053import com.unboundid.ldap.sdk.ToCodeArgHelper; 054import com.unboundid.ldap.sdk.ToCodeHelper; 055import com.unboundid.ldap.sdk.unboundidds.extensions. 056 DeregisterYubiKeyOTPDeviceExtendedRequest; 057import com.unboundid.ldap.sdk.unboundidds.extensions. 058 RegisterYubiKeyOTPDeviceExtendedRequest; 059import com.unboundid.util.Debug; 060import com.unboundid.util.NotMutable; 061import com.unboundid.util.NotNull; 062import com.unboundid.util.Nullable; 063import com.unboundid.util.StaticUtils; 064import com.unboundid.util.ThreadSafety; 065import com.unboundid.util.ThreadSafetyLevel; 066import com.unboundid.util.Validator; 067 068import static com.unboundid.ldap.sdk.unboundidds.UnboundIDDSMessages.*; 069 070 071 072/** 073 * This class provides an implementation of a SASL bind request that may be used 074 * to authenticate to a Directory Server using the UNBOUNDID-YUBIKEY-OTP 075 * mechanism. The credentials include at least an authentication ID and a 076 * one-time password generated by a YubiKey device. The request may also 077 * include a static password (which may or may not be required by the server) 078 * and an optional authorization ID. 079 * <BR> 080 * <BLOCKQUOTE> 081 * <B>NOTE:</B> This class, and other classes within the 082 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 083 * supported for use against Ping Identity, UnboundID, and 084 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 085 * for proprietary functionality or for external specifications that are not 086 * considered stable or mature enough to be guaranteed to work in an 087 * interoperable way with other types of LDAP servers. 088 * </BLOCKQUOTE> 089 * <BR> 090 * The UNBOUNDID-YUBIKEY-OTP bind request MUST include SASL credentials with the 091 * following ASN.1 encoding: 092 * <BR><BR> 093 * <PRE> 094 * UnboundIDYubiKeyCredentials ::= SEQUENCE { 095 * authenticationID [0] OCTET STRING, 096 * authorizationID [1] OCTET STRING OPTIONAL, 097 * staticPassword [2] OCTET STRING OPTIONAL, 098 * yubiKeyOTP [3] OCTET STRING, 099 * ... } 100 * </PRE> 101 * 102 * 103 * @see RegisterYubiKeyOTPDeviceExtendedRequest 104 * @see DeregisterYubiKeyOTPDeviceExtendedRequest 105 */ 106@NotMutable() 107@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 108public final class UnboundIDYubiKeyOTPBindRequest 109 extends SASLBindRequest 110{ 111 /** 112 * The name for the UnboundID YubiKey SASL mechanism. 113 */ 114 @NotNull public static final String UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME = 115 "UNBOUNDID-YUBIKEY-OTP"; 116 117 118 119 /** 120 * The BER type for the authentication ID element of the credentials sequence. 121 */ 122 private static final byte TYPE_AUTHENTICATION_ID = (byte) 0x80; 123 124 125 126 /** 127 * The BER type for the authorization ID element of the credentials sequence. 128 */ 129 private static final byte TYPE_AUTHORIZATION_ID = (byte) 0x81; 130 131 132 133 /** 134 * The BER type for the static password element of the credentials sequence. 135 */ 136 private static final byte TYPE_STATIC_PASSWORD = (byte) 0x82; 137 138 139 140 /** 141 * The BER type for the YubiKey OTP element of the credentials sequence. 142 */ 143 private static final byte TYPE_YUBIKEY_OTP = (byte) 0x83; 144 145 146 147 /** 148 * The serial version UID for this serializable class. 149 */ 150 private static final long serialVersionUID = -6124016046606933247L; 151 152 153 154 // The static password for the user, if provided. 155 @Nullable private final ASN1OctetString staticPassword; 156 157 // The message ID from the last LDAP message sent from this request. 158 private volatile int messageID = -1; 159 160 // The authentication ID for the user. 161 @NotNull private final String authenticationID; 162 163 // The authorization ID for the bind request, if provided. 164 @Nullable private final String authorizationID; 165 166 // The one-time password generated by a YubiKey device. 167 @NotNull private final String yubiKeyOTP; 168 169 170 171 /** 172 * Creates a new UNBOUNDID-YUBIKEY-OTP bind request with the provided 173 * information. 174 * 175 * @param authenticationID The authentication ID for the bind request. It 176 * must not be {@code null}, and must have the form 177 * "dn:" followed by the DN of the target user or 178 * "u:" followed by the the username of the target 179 * user. 180 * @param authorizationID The authorization ID for the bind request. It 181 * may be {@code null} if the authorization identity 182 * should be the same as the authentication 183 * identity. 184 * @param staticPassword The static password for the user specified as the 185 * authentication identity. It may be {@code null} 186 * if authentication should be performed using only 187 * the YubiKey OTP. 188 * @param yubiKeyOTP The one-time password generated by the YubiKey 189 * device. It must not be {@code null}. 190 * @param controls The set of controls to include in the bind 191 * request. It may be {@code null} or empty if 192 * there should not be any request controls. 193 */ 194 public UnboundIDYubiKeyOTPBindRequest(@NotNull final String authenticationID, 195 @Nullable final String authorizationID, 196 @Nullable final String staticPassword, 197 @NotNull final String yubiKeyOTP, 198 @Nullable final Control... controls) 199 { 200 this(authenticationID, authorizationID, toASN1OctetString(staticPassword), 201 yubiKeyOTP, controls); 202 } 203 204 205 206 /** 207 * Creates a new UNBOUNDID-YUBIKEY-OTP bind request with the provided 208 * information. 209 * 210 * @param authenticationID The authentication ID for the bind request. It 211 * must not be {@code null}, and must have the form 212 * "dn:" followed by the DN of the target user or 213 * "u:" followed by the the username of the target 214 * user. 215 * @param authorizationID The authorization ID for the bind request. It 216 * may be {@code null} if the authorization identity 217 * should be the same as the authentication 218 * identity. 219 * @param staticPassword The static password for the user specified as the 220 * authentication identity. It may be {@code null} 221 * if authentication should be performed using only 222 * the YubiKey OTP. 223 * @param yubiKeyOTP The one-time password generated by the YubiKey 224 * device. It must not be {@code null}. 225 * @param controls The set of controls to include in the bind 226 * request. It may be {@code null} or empty if 227 * there should not be any request controls. 228 */ 229 public UnboundIDYubiKeyOTPBindRequest(@NotNull final String authenticationID, 230 @Nullable final String authorizationID, 231 @Nullable final byte[] staticPassword, 232 @NotNull final String yubiKeyOTP, 233 @Nullable final Control... controls) 234 { 235 this(authenticationID, authorizationID, toASN1OctetString(staticPassword), 236 yubiKeyOTP, controls); 237 } 238 239 240 241 /** 242 * Creates a new UNBOUNDID-YUBIKEY-OTP bind request with the provided 243 * information. 244 * 245 * @param authenticationID The authentication ID for the bind request. It 246 * must not be {@code null}, and must have the form 247 * "dn:" followed by the DN of the target user or 248 * "u:" followed by the the username of the target 249 * user. 250 * @param authorizationID The authorization ID for the bind request. It 251 * may be {@code null} if the authorization identity 252 * should be the same as the authentication 253 * identity. 254 * @param staticPassword The static password for the user specified as the 255 * authentication identity. It may be {@code null} 256 * if authentication should be performed using only 257 * the YubiKey OTP. 258 * @param yubiKeyOTP The one-time password generated by the YubiKey 259 * device. It must not be {@code null}. 260 * @param controls The set of controls to include in the bind 261 * request. It may be {@code null} or empty if 262 * there should not be any request controls. 263 */ 264 private UnboundIDYubiKeyOTPBindRequest(@NotNull final String authenticationID, 265 @Nullable final String authorizationID, 266 @Nullable final ASN1OctetString staticPassword, 267 @NotNull final String yubiKeyOTP, 268 @Nullable final Control... controls) 269 { 270 super(controls); 271 272 Validator.ensureNotNull(authenticationID); 273 Validator.ensureNotNull(yubiKeyOTP); 274 275 this.authenticationID = authenticationID; 276 this.authorizationID = authorizationID; 277 this.staticPassword = staticPassword; 278 this.yubiKeyOTP = yubiKeyOTP; 279 } 280 281 282 283 /** 284 * Retrieves an ASN.1 octet string that represents the appropriate encoding 285 * for the provided password. 286 * 287 * @param password The password object to convert to an ASN.1 octet string. 288 * It may be {@code null} if no static password is required. 289 * Otherwise, it must either be a string or a byte array. 290 * 291 * @return The ASN.1 octet string created from the provided password object, 292 * or {@code null} if the provided password object was null. 293 */ 294 @Nullable() 295 private static ASN1OctetString toASN1OctetString( 296 @Nullable final Object password) 297 { 298 if (password == null) 299 { 300 return null; 301 } 302 else if (password instanceof byte[]) 303 { 304 return new ASN1OctetString(TYPE_STATIC_PASSWORD, (byte[]) password); 305 } 306 else 307 { 308 return new ASN1OctetString(TYPE_STATIC_PASSWORD, 309 String.valueOf(password)); 310 } 311 } 312 313 314 315 /** 316 * Creates a new UNBOUNDID-YUBIKEY-OTP SASL bind request decoded from the 317 * provided SASL credentials. 318 * 319 * @param saslCredentials The SASL credentials to decode in order to create 320 * the UNBOUNDID-YUBIKEY-OTP SASL bind request. It 321 * must not be {@code null}. 322 * @param controls The set of controls to include in the bind 323 * request. This may be {@code null} or empty if no 324 * controls should be included in the request. 325 * 326 * @return The UNBOUNDID-YUBIKEY-OTP SASL bind request decoded from the 327 * provided credentials. 328 * 329 * @throws LDAPException If the provided credentials cannot be decoded to a 330 * valid UNBOUNDID-YUBIKEY-OTP bind request. 331 */ 332 @NotNull() 333 public static UnboundIDYubiKeyOTPBindRequest decodeCredentials( 334 @NotNull final ASN1OctetString saslCredentials, 335 @Nullable final Control... controls) 336 throws LDAPException 337 { 338 try 339 { 340 ASN1OctetString staticPassword = null; 341 String authenticationID = null; 342 String authorizationID = null; 343 String yubiKeyOTP = null; 344 345 for (final ASN1Element e : 346 ASN1Sequence.decodeAsSequence(saslCredentials.getValue()).elements()) 347 { 348 switch (e.getType()) 349 { 350 case TYPE_AUTHENTICATION_ID: 351 authenticationID = 352 ASN1OctetString.decodeAsOctetString(e).stringValue(); 353 break; 354 case TYPE_AUTHORIZATION_ID: 355 authorizationID = 356 ASN1OctetString.decodeAsOctetString(e).stringValue(); 357 break; 358 case TYPE_STATIC_PASSWORD: 359 staticPassword = ASN1OctetString.decodeAsOctetString(e); 360 break; 361 case TYPE_YUBIKEY_OTP: 362 yubiKeyOTP = ASN1OctetString.decodeAsOctetString(e).stringValue(); 363 break; 364 default: 365 throw new LDAPException(ResultCode.DECODING_ERROR, 366 ERR_YUBIKEY_OTP_DECODE_UNRECOGNIZED_CRED_ELEMENT.get( 367 UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME, 368 StaticUtils.toHex(e.getType()))); 369 } 370 } 371 372 if (authenticationID == null) 373 { 374 throw new LDAPException(ResultCode.DECODING_ERROR, 375 ERR_YUBIKEY_OTP_DECODE_NO_AUTH_ID.get( 376 UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME)); 377 } 378 379 if (yubiKeyOTP == null) 380 { 381 throw new LDAPException(ResultCode.DECODING_ERROR, 382 ERR_YUBIKEY_OTP_NO_OTP.get(UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME)); 383 } 384 385 return new UnboundIDYubiKeyOTPBindRequest(authenticationID, 386 authorizationID, staticPassword, yubiKeyOTP, controls); 387 } 388 catch (final LDAPException le) 389 { 390 Debug.debugException(le); 391 throw le; 392 } 393 catch (final Exception e) 394 { 395 Debug.debugException(e); 396 throw new LDAPException(ResultCode.DECODING_ERROR, 397 ERR_YUBIKEY_OTP_DECODE_ERROR.get( 398 UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME, 399 StaticUtils.getExceptionMessage(e)), 400 e); 401 } 402 } 403 404 405 406 /** 407 * Retrieves the authentication ID for the bind request. 408 * 409 * @return The authentication ID for the bind request. 410 */ 411 @NotNull() 412 public String getAuthenticationID() 413 { 414 return authenticationID; 415 } 416 417 418 419 /** 420 * Retrieves the authorization ID for the bind request, if any. 421 * 422 * @return The authorization ID for the bind request, or {@code null} if the 423 * authorization identity should match the authentication identity. 424 */ 425 @Nullable() 426 public String getAuthorizationID() 427 { 428 return authorizationID; 429 } 430 431 432 433 /** 434 * Retrieves the string representation of the static password for the bind 435 * request, if any. 436 * 437 * @return The string representation of the static password for the bind 438 * request, or {@code null} if there is no static password. 439 */ 440 @Nullable() 441 public String getStaticPasswordString() 442 { 443 if (staticPassword == null) 444 { 445 return null; 446 } 447 else 448 { 449 return staticPassword.stringValue(); 450 } 451 } 452 453 454 455 /** 456 * Retrieves the bytes that comprise the static password for the bind request, 457 * if any. 458 * 459 * @return The bytes that comprise the static password for the bind request, 460 * or {@code null} if there is no static password. 461 */ 462 @Nullable() 463 public byte[] getStaticPasswordBytes() 464 { 465 if (staticPassword == null) 466 { 467 return null; 468 } 469 else 470 { 471 return staticPassword.getValue(); 472 } 473 } 474 475 476 477 /** 478 * Retrieves the YubiKey-generated one-time password to include in the bind 479 * request. 480 * 481 * @return The YubiKey-generated one-time password to include in the bind 482 * request. 483 */ 484 @NotNull() 485 public String getYubiKeyOTP() 486 { 487 return yubiKeyOTP; 488 } 489 490 491 492 /** 493 * Sends this bind request to the target server over the provided connection 494 * and returns the corresponding response. 495 * 496 * @param connection The connection to use to send this bind request to the 497 * server and read the associated response. 498 * @param depth The current referral depth for this request. It should 499 * always be one for the initial request, and should only 500 * be incremented when following referrals. 501 * 502 * @return The bind response read from the server. 503 * 504 * @throws LDAPException If a problem occurs while sending the request or 505 * reading the response. 506 */ 507 @Override() 508 @NotNull() 509 protected BindResult process(@NotNull final LDAPConnection connection, 510 final int depth) 511 throws LDAPException 512 { 513 setReferralDepth(depth); 514 515 messageID = InternalSDKHelper.nextMessageID(connection); 516 return sendBindRequest(connection, "", encodeCredentials(), getControls(), 517 getResponseTimeoutMillis(connection)); 518 } 519 520 521 522 /** 523 * Retrieves an ASN.1 octet string containing the encoded credentials for this 524 * bind request. 525 * 526 * @return An ASN.1 octet string containing the encoded credentials for this 527 * bind request. 528 */ 529 @NotNull() 530 public ASN1OctetString encodeCredentials() 531 { 532 return encodeCredentials(authenticationID, authorizationID, staticPassword, 533 yubiKeyOTP); 534 } 535 536 537 538 /** 539 * Encodes the provided information into an ASN.1 octet string suitable for 540 * use as the SASL credentials for an UNBOUNDID-YUBIKEY-OTP bind request. 541 * 542 * @param authenticationID The authentication ID for the bind request. It 543 * must not be {@code null}, and must have the form 544 * "dn:" followed by the DN of the target user or 545 * "u:" followed by the the username of the target 546 * user. 547 * @param authorizationID The authorization ID for the bind request. It 548 * may be {@code null} if the authorization identity 549 * should be the same as the authentication 550 * identity. 551 * @param staticPassword The static password for the user specified as the 552 * authentication identity. It may be {@code null} 553 * if authentication should be performed using only 554 * the YubiKey OTP. 555 * @param yubiKeyOTP The one-time password generated by the YubiKey 556 * device. It must not be {@code null}. 557 * 558 * @return An ASN.1 octet string suitable for use as the SASL credentials for 559 * an UNBOUNDID-YUBIKEY-OTP bind request. 560 */ 561 @NotNull() 562 public static ASN1OctetString encodeCredentials( 563 @NotNull final String authenticationID, 564 @Nullable final String authorizationID, 565 @Nullable final ASN1OctetString staticPassword, 566 @NotNull final String yubiKeyOTP) 567 { 568 Validator.ensureNotNull(authenticationID); 569 Validator.ensureNotNull(yubiKeyOTP); 570 571 final ArrayList<ASN1Element> elements = new ArrayList<>(4); 572 elements.add(new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID)); 573 574 if (authorizationID != null) 575 { 576 elements.add(new ASN1OctetString(TYPE_AUTHORIZATION_ID, authorizationID)); 577 } 578 579 if (staticPassword != null) 580 { 581 elements.add(new ASN1OctetString(TYPE_STATIC_PASSWORD, 582 staticPassword.getValue())); 583 } 584 585 elements.add(new ASN1OctetString(TYPE_YUBIKEY_OTP, yubiKeyOTP)); 586 587 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 588 } 589 590 591 592 /** 593 * {@inheritDoc} 594 */ 595 @Override() 596 @NotNull() 597 public UnboundIDYubiKeyOTPBindRequest duplicate() 598 { 599 return duplicate(getControls()); 600 } 601 602 603 604 /** 605 * {@inheritDoc} 606 */ 607 @Override() 608 @NotNull() 609 public UnboundIDYubiKeyOTPBindRequest duplicate( 610 @Nullable final Control[] controls) 611 { 612 final UnboundIDYubiKeyOTPBindRequest bindRequest = 613 new UnboundIDYubiKeyOTPBindRequest(authenticationID, authorizationID, 614 staticPassword, yubiKeyOTP, controls); 615 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 616 bindRequest.setIntermediateResponseListener( 617 getIntermediateResponseListener()); 618 bindRequest.setReferralDepth(getReferralDepth()); 619 bindRequest.setReferralConnector(getReferralConnectorInternal()); 620 return bindRequest; 621 } 622 623 624 625 /** 626 * {@inheritDoc} 627 */ 628 @Override() 629 @NotNull() 630 public String getSASLMechanismName() 631 { 632 return UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME; 633 } 634 635 636 637 /** 638 * {@inheritDoc} 639 */ 640 @Override() 641 public int getLastMessageID() 642 { 643 return messageID; 644 } 645 646 647 648 /** 649 * {@inheritDoc} 650 */ 651 @Override() 652 public void toString(@NotNull final StringBuilder buffer) 653 { 654 buffer.append("UnboundYubiKeyOTPBindRequest(authenticationID='"); 655 buffer.append(authenticationID); 656 657 if (authorizationID != null) 658 { 659 buffer.append("', authorizationID='"); 660 buffer.append(authorizationID); 661 } 662 663 buffer.append("', staticPasswordProvided="); 664 buffer.append(staticPassword != null); 665 666 final Control[] controls = getControls(); 667 if (controls.length > 0) 668 { 669 buffer.append(", controls={"); 670 for (int i=0; i < controls.length; i++) 671 { 672 if (i > 0) 673 { 674 buffer.append(", "); 675 } 676 677 buffer.append(controls[i]); 678 } 679 buffer.append('}'); 680 } 681 682 buffer.append(')'); 683 } 684 685 686 687 /** 688 * {@inheritDoc} 689 */ 690 @Override() 691 public void toCode(@NotNull final List<String> lineList, 692 @NotNull final String requestID, 693 final int indentSpaces, final boolean includeProcessing) 694 { 695 // Create the request variable. 696 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(5); 697 constructorArgs.add(ToCodeArgHelper.createString(getAuthenticationID(), 698 "Authentication ID")); 699 constructorArgs.add(ToCodeArgHelper.createString(getAuthorizationID(), 700 "Authorization ID")); 701 constructorArgs.add(ToCodeArgHelper.createString( 702 "---redacted-static-password---", "Static Password")); 703 constructorArgs.add(ToCodeArgHelper.createString( 704 ((getStaticPasswordString() == null) 705 ? "null" 706 : "---redacted-static-password---"), 707 "Static Password")); 708 constructorArgs.add(ToCodeArgHelper.createString( 709 "---redacted-yubikey-otp---", "YubiKey OTP")); 710 711 final Control[] controls = getControls(); 712 if (controls.length > 0) 713 { 714 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 715 "Bind Controls")); 716 } 717 718 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 719 "UnboundIDYubiKeyOTPBindRequest", requestID + "Request", 720 "new UnboundIDYubiKeyOTPBindRequest", constructorArgs); 721 722 723 // Add lines for processing the request and obtaining the result. 724 if (includeProcessing) 725 { 726 // Generate a string with the appropriate indent. 727 final StringBuilder buffer = new StringBuilder(); 728 for (int i=0; i < indentSpaces; i++) 729 { 730 buffer.append(' '); 731 } 732 final String indent = buffer.toString(); 733 734 lineList.add(""); 735 lineList.add(indent + "try"); 736 lineList.add(indent + '{'); 737 lineList.add(indent + " BindResult " + requestID + 738 "Result = connection.bind(" + requestID + "Request);"); 739 lineList.add(indent + " // The bind was processed successfully."); 740 lineList.add(indent + '}'); 741 lineList.add(indent + "catch (LDAPException e)"); 742 lineList.add(indent + '{'); 743 lineList.add(indent + " // The bind failed. Maybe the following will " + 744 "help explain why."); 745 lineList.add(indent + " // Note that the connection is now likely in " + 746 "an unauthenticated state."); 747 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 748 lineList.add(indent + " String message = e.getMessage();"); 749 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 750 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 751 lineList.add(indent + " Control[] responseControls = " + 752 "e.getResponseControls();"); 753 lineList.add(indent + '}'); 754 } 755 } 756}