001/* 002 * Copyright 2016-2022 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2016-2022 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-2022 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 messageID = InternalSDKHelper.nextMessageID(connection); 514 return sendBindRequest(connection, "", encodeCredentials(), getControls(), 515 getResponseTimeoutMillis(connection)); 516 } 517 518 519 520 /** 521 * Retrieves an ASN.1 octet string containing the encoded credentials for this 522 * bind request. 523 * 524 * @return An ASN.1 octet string containing the encoded credentials for this 525 * bind request. 526 */ 527 @NotNull() 528 public ASN1OctetString encodeCredentials() 529 { 530 return encodeCredentials(authenticationID, authorizationID, staticPassword, 531 yubiKeyOTP); 532 } 533 534 535 536 /** 537 * Encodes the provided information into an ASN.1 octet string suitable for 538 * use as the SASL credentials for an UNBOUNDID-YUBIKEY-OTP bind request. 539 * 540 * @param authenticationID The authentication ID for the bind request. It 541 * must not be {@code null}, and must have the form 542 * "dn:" followed by the DN of the target user or 543 * "u:" followed by the the username of the target 544 * user. 545 * @param authorizationID The authorization ID for the bind request. It 546 * may be {@code null} if the authorization identity 547 * should be the same as the authentication 548 * identity. 549 * @param staticPassword The static password for the user specified as the 550 * authentication identity. It may be {@code null} 551 * if authentication should be performed using only 552 * the YubiKey OTP. 553 * @param yubiKeyOTP The one-time password generated by the YubiKey 554 * device. It must not be {@code null}. 555 * 556 * @return An ASN.1 octet string suitable for use as the SASL credentials for 557 * an UNBOUNDID-YUBIKEY-OTP bind request. 558 */ 559 @NotNull() 560 public static ASN1OctetString encodeCredentials( 561 @NotNull final String authenticationID, 562 @Nullable final String authorizationID, 563 @Nullable final ASN1OctetString staticPassword, 564 @NotNull final String yubiKeyOTP) 565 { 566 Validator.ensureNotNull(authenticationID); 567 Validator.ensureNotNull(yubiKeyOTP); 568 569 final ArrayList<ASN1Element> elements = new ArrayList<>(4); 570 elements.add(new ASN1OctetString(TYPE_AUTHENTICATION_ID, authenticationID)); 571 572 if (authorizationID != null) 573 { 574 elements.add(new ASN1OctetString(TYPE_AUTHORIZATION_ID, authorizationID)); 575 } 576 577 if (staticPassword != null) 578 { 579 elements.add(new ASN1OctetString(TYPE_STATIC_PASSWORD, 580 staticPassword.getValue())); 581 } 582 583 elements.add(new ASN1OctetString(TYPE_YUBIKEY_OTP, yubiKeyOTP)); 584 585 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 586 } 587 588 589 590 /** 591 * {@inheritDoc} 592 */ 593 @Override() 594 @NotNull() 595 public UnboundIDYubiKeyOTPBindRequest duplicate() 596 { 597 return duplicate(getControls()); 598 } 599 600 601 602 /** 603 * {@inheritDoc} 604 */ 605 @Override() 606 @NotNull() 607 public UnboundIDYubiKeyOTPBindRequest duplicate( 608 @Nullable final Control[] controls) 609 { 610 final UnboundIDYubiKeyOTPBindRequest bindRequest = 611 new UnboundIDYubiKeyOTPBindRequest(authenticationID, authorizationID, 612 staticPassword, yubiKeyOTP, controls); 613 bindRequest.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 614 return bindRequest; 615 } 616 617 618 619 /** 620 * {@inheritDoc} 621 */ 622 @Override() 623 @NotNull() 624 public String getSASLMechanismName() 625 { 626 return UNBOUNDID_YUBIKEY_OTP_MECHANISM_NAME; 627 } 628 629 630 631 /** 632 * {@inheritDoc} 633 */ 634 @Override() 635 public int getLastMessageID() 636 { 637 return messageID; 638 } 639 640 641 642 /** 643 * {@inheritDoc} 644 */ 645 @Override() 646 public void toString(@NotNull final StringBuilder buffer) 647 { 648 buffer.append("UnboundYubiKeyOTPBindRequest(authenticationID='"); 649 buffer.append(authenticationID); 650 651 if (authorizationID != null) 652 { 653 buffer.append("', authorizationID='"); 654 buffer.append(authorizationID); 655 } 656 657 buffer.append("', staticPasswordProvided="); 658 buffer.append(staticPassword != null); 659 660 final Control[] controls = getControls(); 661 if (controls.length > 0) 662 { 663 buffer.append(", controls={"); 664 for (int i=0; i < controls.length; i++) 665 { 666 if (i > 0) 667 { 668 buffer.append(", "); 669 } 670 671 buffer.append(controls[i]); 672 } 673 buffer.append('}'); 674 } 675 676 buffer.append(')'); 677 } 678 679 680 681 /** 682 * {@inheritDoc} 683 */ 684 @Override() 685 public void toCode(@NotNull final List<String> lineList, 686 @NotNull final String requestID, 687 final int indentSpaces, final boolean includeProcessing) 688 { 689 // Create the request variable. 690 final ArrayList<ToCodeArgHelper> constructorArgs = new ArrayList<>(5); 691 constructorArgs.add(ToCodeArgHelper.createString(getAuthenticationID(), 692 "Authentication ID")); 693 constructorArgs.add(ToCodeArgHelper.createString(getAuthorizationID(), 694 "Authorization ID")); 695 constructorArgs.add(ToCodeArgHelper.createString( 696 "---redacted-static-password---", "Static Password")); 697 constructorArgs.add(ToCodeArgHelper.createString( 698 ((getStaticPasswordString() == null) 699 ? "null" 700 : "---redacted-static-password---"), 701 "Static Password")); 702 constructorArgs.add(ToCodeArgHelper.createString( 703 "---redacted-yubikey-otp---", "YubiKey OTP")); 704 705 final Control[] controls = getControls(); 706 if (controls.length > 0) 707 { 708 constructorArgs.add(ToCodeArgHelper.createControlArray(controls, 709 "Bind Controls")); 710 } 711 712 ToCodeHelper.generateMethodCall(lineList, indentSpaces, 713 "UnboundIDYubiKeyOTPBindRequest", requestID + "Request", 714 "new UnboundIDYubiKeyOTPBindRequest", constructorArgs); 715 716 717 // Add lines for processing the request and obtaining the result. 718 if (includeProcessing) 719 { 720 // Generate a string with the appropriate indent. 721 final StringBuilder buffer = new StringBuilder(); 722 for (int i=0; i < indentSpaces; i++) 723 { 724 buffer.append(' '); 725 } 726 final String indent = buffer.toString(); 727 728 lineList.add(""); 729 lineList.add(indent + "try"); 730 lineList.add(indent + '{'); 731 lineList.add(indent + " BindResult " + requestID + 732 "Result = connection.bind(" + requestID + "Request);"); 733 lineList.add(indent + " // The bind was processed successfully."); 734 lineList.add(indent + '}'); 735 lineList.add(indent + "catch (LDAPException e)"); 736 lineList.add(indent + '{'); 737 lineList.add(indent + " // The bind failed. Maybe the following will " + 738 "help explain why."); 739 lineList.add(indent + " // Note that the connection is now likely in " + 740 "an unauthenticated state."); 741 lineList.add(indent + " ResultCode resultCode = e.getResultCode();"); 742 lineList.add(indent + " String message = e.getMessage();"); 743 lineList.add(indent + " String matchedDN = e.getMatchedDN();"); 744 lineList.add(indent + " String[] referralURLs = e.getReferralURLs();"); 745 lineList.add(indent + " Control[] responseControls = " + 746 "e.getResponseControls();"); 747 lineList.add(indent + '}'); 748 } 749 } 750}