001 /* 002 * Copyright 2013-2015 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2015 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021 package com.unboundid.ldap.sdk.unboundidds.extensions; 022 023 024 025 import java.util.ArrayList; 026 import java.util.Collections; 027 import java.util.Iterator; 028 import java.util.LinkedHashSet; 029 import java.util.List; 030 031 import com.unboundid.asn1.ASN1Element; 032 import com.unboundid.asn1.ASN1OctetString; 033 import com.unboundid.asn1.ASN1Sequence; 034 import com.unboundid.ldap.sdk.Control; 035 import com.unboundid.ldap.sdk.ExtendedRequest; 036 import com.unboundid.ldap.sdk.ExtendedResult; 037 import com.unboundid.ldap.sdk.LDAPConnection; 038 import com.unboundid.ldap.sdk.LDAPException; 039 import com.unboundid.ldap.sdk.ResultCode; 040 import com.unboundid.util.Debug; 041 import com.unboundid.util.NotMutable; 042 import com.unboundid.util.ObjectPair; 043 import com.unboundid.util.StaticUtils; 044 import com.unboundid.util.ThreadSafety; 045 import com.unboundid.util.ThreadSafetyLevel; 046 047 import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 048 049 050 051 /** 052 * <BLOCKQUOTE> 053 * <B>NOTE:</B> This class is part of the Commercial Edition of the UnboundID 054 * LDAP SDK for Java. It is not available for use in applications that 055 * include only the Standard Edition of the LDAP SDK, and is not supported for 056 * use in conjunction with non-UnboundID products. 057 * </BLOCKQUOTE> 058 * This class provides an implementation of an extended request that may be used 059 * to request that the Directory Server deliver a one-time password to an end 060 * user that they may use to authenticate via an 061 * {@link com.unboundid.ldap.sdk.unboundidds.UnboundIDDeliveredOTPBindRequest}. 062 * <BR><BR> 063 * Notes on the recommended use of this extended request: 064 * <UL> 065 * <LI>Whenever possible, the user's static password should be provided. 066 * However, the server will allow the static password to be omitted if the 067 * authentication ID included in the request matches the authorization 068 * identity of the extended operation (either because that user is already 069 * authenticated on the connection, or because the request includes a 070 * proxied authorization or intermediate client control specifying that 071 * identity). In that case, the operation will be able to act as a 072 * "step-up" mechanism, providing further proof of the identity of an 073 * already-authenticated client rather than performing the complete 074 * authentication process.</LI> 075 * <LI>The request offers two mechanisms for indicating which delivery 076 * mechanism(s) should be considered: an option to specify just the 077 * delivery mechanism names, and an option to specify the names along with 078 * recipient IDs. At most one of these elements must be present in the 079 * request. If neither is present, the server will attempt to determine 080 * which delivery mechanisms and recipient IDs should be used. If the 081 * set of preferred delivery mechanisms includes multiple items, the 082 * server will attempt them in the order provided until it is able to 083 * successfully deliver the message. The server will not attempt to 084 * use any other delivery mechanisms that may be configured if the request 085 * includes a list of preferred delivery mechanisms.</LI> 086 * <LI>Although the message elements (message subject, and full and compact 087 * text before and after the OTP) are optional, it is recommended that 088 * they be supplied by the client. The server will provide a generic 089 * message if no message elements are included in the request.</LI> 090 * </UL> 091 * <BR><BR> 092 * The OID for this extended request is 1.3.6.1.4.1.30221.2.6.24. It must have 093 * a value, and that value should have the following encoding: 094 * <BR><BR> 095 * <PRE> 096 * DeliverOTPRequest ::= SEQUENCE { 097 * authenticationID [0] OCTET STRING, 098 * staticPassword [1] OCTET STRING OPTIONAL, 099 * preferredMechNames [2] SEQUENCE OF OCTET STRING OPTIONAL, 100 * preferredMechNamesAndIDs [3] SEQUENCE OF SEQUENCE, 101 * mechanismName OCTET STRING, 102 * recipientID OCTET STRING OPTIONAL } OPTIONAL, 103 * messageSubject [4] OCTET STRING OPTIONAL, 104 * fullTextBeforeOTP [5] OCTET STRING OPTIONAL, 105 * fullTextAfterOTP [6] OCTET STRING OPTIONAL, 106 * compactTextBeforeOTP [7] OCTET STRING OPTIONAL, 107 * compactTextAfterOTP [8] OCTET STRING OPTIONAL, 108 * ... } 109 * </PRE> 110 * 111 * @see com.unboundid.ldap.sdk.unboundidds.UnboundIDDeliveredOTPBindRequest 112 * @see DeliverOneTimePasswordExtendedResult 113 */ 114 @NotMutable() 115 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 116 public final class DeliverOneTimePasswordExtendedRequest 117 extends ExtendedRequest 118 { 119 /** 120 * The OID (1.3.6.1.4.1.30221.2.6.24) for the deliver one-time password 121 * extended request. 122 */ 123 public static final String DELIVER_OTP_REQUEST_OID = 124 "1.3.6.1.4.1.30221.2.6.24"; 125 126 127 128 /** 129 * The BER type for the authentication ID element. 130 */ 131 private static final byte TYPE_AUTHN_ID = (byte) 0x80; 132 133 134 135 /** 136 * The BER type for the static password element. 137 */ 138 private static final byte TYPE_PASSWORD = (byte) 0x81; 139 140 141 142 /** 143 * The BER type for the preferred delivery mechanism names element.. 144 */ 145 private static final byte TYPE_PREFERRED_DELIVERY_MECHANISM_NAMES = 146 (byte) 0xA2; 147 148 149 150 /** 151 * The BER type for the preferred delivery mechanism names and IDs element.. 152 */ 153 private static final byte TYPE_PREFERRED_DELIVERY_MECHANISM_NAMES_AND_IDS = 154 (byte) 0xA3; 155 156 157 158 /** 159 * The BER type for the "message subject" element of the value sequence. 160 */ 161 private static final byte MESSAGE_SUBJECT_BER_TYPE = (byte) 0x84; 162 163 164 165 /** 166 * The BER type for the "full text before OTP" element of the value 167 * sequence. 168 */ 169 private static final byte FULL_TEXT_BEFORE_OTP_BER_TYPE = (byte) 0x85; 170 171 172 173 /** 174 * The BER type for the "full text after OTP" element of the value 175 * sequence. 176 */ 177 private static final byte FULL_TEXT_AFTER_OTP_BER_TYPE = (byte) 0x86; 178 179 180 181 /** 182 * The BER type for the "compact text before OTP" element of the value 183 * sequence. 184 */ 185 private static final byte COMPACT_TEXT_BEFORE_OTP_BER_TYPE = (byte) 0x87; 186 187 188 189 /** 190 * The BER type for the "compact text after OTP" element of the value 191 * sequence. 192 */ 193 private static final byte COMPACT_TEXT_AFTER_OTP_BER_TYPE = (byte) 0x88; 194 195 196 197 /** 198 * The serial version UID for this serializable class. 199 */ 200 private static final long serialVersionUID = 1259250969726758847L; 201 202 203 204 // The static password to include in the request. 205 private final ASN1OctetString staticPassword; 206 207 // The list of preferred delivery mechanisms to include in the request. 208 private final List<ObjectPair<String, String>> preferredDeliveryMechanisms; 209 210 // The authentication ID to include in the request. 211 private final String authenticationID; 212 213 // The text to include after the OTP in a compact message. 214 private final String compactTextAfterOTP; 215 216 // The text to include before the OTP in a compact message. 217 private final String compactTextBeforeOTP; 218 219 // The text to include after the OTP in a message without size constraints. 220 private final String fullTextAfterOTP; 221 222 // The text to include before the OTP in a message without size constraints. 223 private final String fullTextBeforeOTP; 224 225 // The text to use as the message subject. 226 private final String messageSubject; 227 228 229 230 /** 231 * Creates a new deliver one-time password extended request with the provided 232 * information. 233 * 234 * @param authenticationID The authentication ID for the user to 235 * whom the one-time password should be 236 * delivered. It must not be 237 * {@code null}. 238 * @param staticPassword The static password for the user to 239 * whom the one-time password should be 240 * delivered. It may be {@code null} if 241 * this request is intended to be used 242 * to step-up an existing authentication 243 * rather than perform a new 244 * authentication (in which case the 245 * provided authentication ID must match 246 * the operation's authorization ID). 247 * @param preferredDeliveryMechanisms The names of the preferred delivery 248 * mechanisms for the one-time password. 249 * It may be {@code null} or empty if the 250 * server should select an appropriate 251 * delivery mechanism. If it is 252 * non-{@code null} and non-empty, then 253 * only the listed mechanisms will be 254 * considered for use, even if the server 255 * supports alternate mechanisms not 256 * included in this list. 257 */ 258 public DeliverOneTimePasswordExtendedRequest(final String authenticationID, 259 final String staticPassword, 260 final String... preferredDeliveryMechanisms) 261 { 262 this(authenticationID, staticPassword, 263 StaticUtils.toList(preferredDeliveryMechanisms)); 264 } 265 266 267 268 /** 269 * Creates a new deliver one-time password extended request with the provided 270 * information. 271 * 272 * @param authenticationID The authentication ID for the user to 273 * whom the one-time password should be 274 * delivered. It must not be 275 * {@code null}. 276 * @param staticPassword The static password for the user to 277 * whom the one-time password should be 278 * delivered. It may be {@code null} if 279 * this request is intended to be used 280 * to step-up an existing authentication 281 * rather than perform a new 282 * authentication (in which case the 283 * provided authentication ID must match 284 * the operation's authorization ID). 285 * @param preferredDeliveryMechanisms The names of the preferred delivery 286 * mechanisms for the one-time password. 287 * It may be {@code null} or empty if the 288 * server should select an appropriate 289 * delivery mechanism. If it is 290 * non-{@code null} and non-empty, then 291 * only the listed mechanisms will be 292 * considered for use, even if the server 293 * supports alternate mechanisms not 294 * included in this list. 295 */ 296 public DeliverOneTimePasswordExtendedRequest(final String authenticationID, 297 final byte[] staticPassword, 298 final String... preferredDeliveryMechanisms) 299 { 300 this(authenticationID, staticPassword, 301 StaticUtils.toList(preferredDeliveryMechanisms)); 302 } 303 304 305 306 /** 307 * Creates a new deliver one-time password extended request with the provided 308 * information. 309 * 310 * @param authenticationID The authentication ID for the user to 311 * whom the one-time password should be 312 * delivered. It must not be 313 * {@code null}. 314 * @param staticPassword The static password for the user to 315 * whom the one-time password should be 316 * delivered. It may be {@code null} if 317 * this request is intended to be used 318 * to step-up an existing authentication 319 * rather than perform a new 320 * authentication (in which case the 321 * provided authentication ID must match 322 * the operation's authorization ID). 323 * @param preferredDeliveryMechanisms The names of the preferred delivery 324 * mechanisms for the one-time password. 325 * It may be {@code null} or empty if the 326 * server should select an appropriate 327 * delivery mechanism. If it is 328 * non-{@code null} and non-empty, then 329 * only the listed mechanisms will be 330 * considered for use, even if the server 331 * supports alternate mechanisms not 332 * included in this list. 333 * @param controls The set of controls to include in the 334 * request. It may be {@code null} or 335 * empty if no controls should be 336 * included. 337 */ 338 public DeliverOneTimePasswordExtendedRequest(final String authenticationID, 339 final String staticPassword, 340 final List<String> preferredDeliveryMechanisms, 341 final Control... controls) 342 { 343 this(authenticationID, 344 (staticPassword == null 345 ? null 346 : new ASN1OctetString(TYPE_PASSWORD, staticPassword)), 347 preferredDeliveryMechanisms, controls); 348 } 349 350 351 352 /** 353 * Creates a new deliver one-time password extended request with the provided 354 * information. 355 * 356 * @param authenticationID The authentication ID for the user to 357 * whom the one-time password should be 358 * delivered. It must not be 359 * {@code null}. 360 * @param staticPassword The static password for the user to 361 * whom the one-time password should be 362 * delivered. It may be {@code null} if 363 * this request is intended to be used 364 * to step-up an existing authentication 365 * rather than perform a new 366 * authentication (in which case the 367 * provided authentication ID must match 368 * the operation's authorization ID). 369 * @param preferredDeliveryMechanisms The names of the preferred delivery 370 * mechanisms for the one-time password. 371 * It may be {@code null} or empty if the 372 * server should select an appropriate 373 * delivery mechanism. If it is 374 * non-{@code null} and non-empty, then 375 * only the listed mechanisms will be 376 * considered for use, even if the server 377 * supports alternate mechanisms not 378 * included in this list. 379 * @param controls The set of controls to include in the 380 * request. It may be {@code null} or 381 * empty if no controls should be 382 * included. 383 */ 384 public DeliverOneTimePasswordExtendedRequest(final String authenticationID, 385 final byte[] staticPassword, 386 final List<String> preferredDeliveryMechanisms, 387 final Control... controls) 388 { 389 this(authenticationID, 390 (staticPassword == null 391 ? null 392 : new ASN1OctetString(TYPE_PASSWORD, staticPassword)), 393 preferredDeliveryMechanisms, controls); 394 } 395 396 397 398 /** 399 * Creates a new deliver one-time password extended request with the provided 400 * information. 401 * 402 * @param authenticationID The authentication ID for the user to 403 * whom the one-time password should be 404 * delivered. It must not be 405 * {@code null}. 406 * @param staticPassword The static password for the user to 407 * whom the one-time password should be 408 * delivered. It may be {@code null} if 409 * this request is intended to be used 410 * to step-up an existing authentication 411 * rather than perform a new 412 * authentication (in which case the 413 * provided authentication ID must match 414 * the operation's authorization ID). 415 * @param preferredDeliveryMechanisms The names of the preferred delivery 416 * mechanisms for the one-time password. 417 * It may be {@code null} or empty if the 418 * server should select an appropriate 419 * delivery mechanism. If it is 420 * non-{@code null} and non-empty, then 421 * only the listed mechanisms will be 422 * considered for use, even if the server 423 * supports alternate mechanisms not 424 * included in this list. 425 * @param controls The set of controls to include in the 426 * request. It may be {@code null} or 427 * empty if no controls should be 428 * included. 429 */ 430 private DeliverOneTimePasswordExtendedRequest(final String authenticationID, 431 final ASN1OctetString staticPassword, 432 final List<String> preferredDeliveryMechanisms, 433 final Control... controls) 434 { 435 super(DELIVER_OTP_REQUEST_OID, 436 encodeValue(authenticationID, staticPassword, 437 preferredDeliveryMechanisms), 438 controls); 439 440 this.authenticationID = authenticationID; 441 this.staticPassword = staticPassword; 442 443 if ((preferredDeliveryMechanisms == null) || 444 preferredDeliveryMechanisms.isEmpty()) 445 { 446 this.preferredDeliveryMechanisms = Collections.emptyList(); 447 } 448 else 449 { 450 final ArrayList<ObjectPair<String,String>> l = 451 new ArrayList<ObjectPair<String,String>>( 452 preferredDeliveryMechanisms.size()); 453 for (final String s : preferredDeliveryMechanisms) 454 { 455 l.add(new ObjectPair<String,String>(s, null)); 456 } 457 this.preferredDeliveryMechanisms = Collections.unmodifiableList(l); 458 } 459 460 messageSubject = null; 461 fullTextBeforeOTP = null; 462 fullTextAfterOTP = null; 463 compactTextBeforeOTP = null; 464 compactTextAfterOTP = null; 465 } 466 467 468 469 /** 470 * Creates a new deliver one-time password extended request with the provided 471 * information. 472 * 473 * @param authenticationID The authentication ID for the user to 474 * whom the one-time password should be 475 * delivered. It must not be 476 * {@code null}. 477 * @param staticPassword The static password for the user to 478 * whom the one-time password should be 479 * delivered. It may be {@code null} if 480 * this request is intended to be used 481 * to step-up an existing authentication 482 * rather than perform a new 483 * authentication (in which case the 484 * provided authentication ID must match 485 * the operation's authorization ID). 486 * @param messageSubject The text (if any) that should be used 487 * as the message subject if the delivery 488 * mechanism accepts a subject. This may 489 * be {@code null} if no subject is 490 * required or a subject should be 491 * automatically generated. 492 * @param fullTextBeforeOTP The text (if any) that should appear 493 * before the generated one-time password 494 * in the message delivered to the user 495 * via a delivery mechanism that does not 496 * impose significant constraints on 497 * message size. This may be 498 * {@code null} if no text is required 499 * before the one-time password. 500 * @param fullTextAfterOTP The text (if any) that should appear 501 * after the one-time password in the 502 * message delivered to the user via a 503 * delivery mechanism that does not 504 * impose significant constraints on 505 * message size. This may be 506 * {@code null} if no text is required 507 * after the one-time password. 508 * @param compactTextBeforeOTP The text (if any) that should appear 509 * before the generated one-time password 510 * in the message delivered to the user 511 * via a delivery mechanism that imposes 512 * significant constraints on message 513 * size. This may be {@code null} if no 514 * text is required before the one-time 515 * password. 516 * @param compactTextAfterOTP The text (if any) that should appear 517 * after the generated one-time password 518 * in the message delivered to the user 519 * via a delivery mechanism that imposes 520 * significant constraints on message 521 * size. This may be {@code null} if no 522 * text is required after the one-time 523 * password. 524 * @param preferredDeliveryMechanisms An optional ordered list of preferred 525 * delivery mechanisms that should be 526 * used to deliver the one-time password 527 * to the user. It may be {@code null} 528 * or empty to allow the server to select 529 * an appropriate delivery mechanism. If 530 * it is non-{@code null} and non-empty, 531 * then only the listed mechanisms will 532 * be considered for use, even if the 533 * server supports alternate mechanisms 534 * not included in this list. Each 535 * {@code ObjectPair} item must have 536 * a non-{@code null} value for the first 537 * element, which is the name of the 538 * target delivery mechanism. It may 539 * optionally have a non-{@code null} 540 * value for the second element, which is 541 * a recipient ID to use for that 542 * mechanism (e.g., the target mobile 543 * phone number for SMS delivery, an 544 * email address for email delivery, 545 * etc.). If no recipient ID is provided 546 * for a mechanism, then the server will 547 * attempt to select a value for the 548 * user. 549 * @param controls The set of controls to include in the 550 * request. It may be {@code null} or 551 * empty if no controls should be 552 * included. 553 */ 554 public DeliverOneTimePasswordExtendedRequest(final String authenticationID, 555 final String staticPassword, final String messageSubject, 556 final String fullTextBeforeOTP, final String fullTextAfterOTP, 557 final String compactTextBeforeOTP, final String compactTextAfterOTP, 558 final List<ObjectPair<String,String>> preferredDeliveryMechanisms, 559 final Control... controls) 560 { 561 this(authenticationID, 562 (staticPassword == null 563 ? null 564 : new ASN1OctetString(TYPE_PASSWORD, staticPassword)), 565 messageSubject, fullTextBeforeOTP, fullTextAfterOTP, 566 compactTextBeforeOTP, compactTextAfterOTP, preferredDeliveryMechanisms, 567 controls); 568 } 569 570 571 572 /** 573 * Creates a new deliver one-time password extended request with the provided 574 * information. 575 * 576 * @param authenticationID The authentication ID for the user to 577 * whom the one-time password should be 578 * delivered. It must not be 579 * {@code null}. 580 * @param staticPassword The static password for the user to 581 * whom the one-time password should be 582 * delivered. It may be {@code null} if 583 * this request is intended to be used 584 * to step-up an existing authentication 585 * rather than perform a new 586 * authentication (in which case the 587 * provided authentication ID must match 588 * the operation's authorization ID). 589 * @param messageSubject The text (if any) that should be used 590 * as the message subject if the delivery 591 * mechanism accepts a subject. This may 592 * be {@code null} if no subject is 593 * required or a subject should be 594 * automatically generated. 595 * @param fullTextBeforeOTP The text (if any) that should appear 596 * before the generated one-time password 597 * in the message delivered to the user 598 * via a delivery mechanism that does not 599 * impose significant constraints on 600 * message size. This may be 601 * {@code null} if no text is required 602 * before the one-time password. 603 * @param fullTextAfterOTP The text (if any) that should appear 604 * after the one-time password in the 605 * message delivered to the user via a 606 * delivery mechanism that does not 607 * impose significant constraints on 608 * message size. This may be 609 * {@code null} if no text is required 610 * after the one-time password. 611 * @param compactTextBeforeOTP The text (if any) that should appear 612 * before the generated one-time password 613 * in the message delivered to the user 614 * via a delivery mechanism that imposes 615 * significant constraints on message 616 * size. This may be {@code null} if no 617 * text is required before the one-time 618 * password. 619 * @param compactTextAfterOTP The text (if any) that should appear 620 * after the generated one-time password 621 * in the message delivered to the user 622 * via a delivery mechanism that imposes 623 * significant constraints on message 624 * size. This may be {@code null} if no 625 * text is required after the one-time 626 * password. 627 * @param preferredDeliveryMechanisms An optional ordered list of preferred 628 * delivery mechanisms that should be 629 * used to deliver the one-time password 630 * to the user. It may be {@code null} 631 * or empty to allow the server to select 632 * an appropriate delivery mechanism. If 633 * it is non-{@code null} and non-empty, 634 * then only the listed mechanisms will 635 * be considered for use, even if the 636 * server supports alternate mechanisms 637 * not included in this list. Each 638 * {@code ObjectPair} item must have 639 * a non-{@code null} value for the first 640 * element, which is the name of the 641 * target delivery mechanism. It may 642 * optionally have a non-{@code null} 643 * value for the second element, which is 644 * a recipient ID to use for that 645 * mechanism (e.g., the target mobile 646 * phone number for SMS delivery, an 647 * email address for email delivery, 648 * etc.). If no recipient ID is provided 649 * for a mechanism, then the server will 650 * attempt to select a value for the 651 * user. 652 * @param controls The set of controls to include in the 653 * request. It may be {@code null} or 654 * empty if no controls should be 655 * included. 656 */ 657 public DeliverOneTimePasswordExtendedRequest(final String authenticationID, 658 final byte[] staticPassword, final String messageSubject, 659 final String fullTextBeforeOTP, final String fullTextAfterOTP, 660 final String compactTextBeforeOTP, final String compactTextAfterOTP, 661 final List<ObjectPair<String,String>> preferredDeliveryMechanisms, 662 final Control... controls) 663 { 664 this(authenticationID, 665 (staticPassword == null 666 ? null 667 : new ASN1OctetString(TYPE_PASSWORD, staticPassword)), 668 messageSubject, fullTextBeforeOTP, fullTextAfterOTP, 669 compactTextBeforeOTP, compactTextAfterOTP, preferredDeliveryMechanisms, 670 controls); 671 } 672 673 674 675 /** 676 * Creates a new deliver one-time password extended request with the provided 677 * information. 678 * 679 * @param authenticationID The authentication ID for the user to 680 * whom the one-time password should be 681 * delivered. It must not be 682 * {@code null}. 683 * @param staticPassword The static password for the user to 684 * whom the one-time password should be 685 * delivered. It may be {@code null} if 686 * this request is intended to be used 687 * to step-up an existing authentication 688 * rather than perform a new 689 * authentication (in which case the 690 * provided authentication ID must match 691 * the operation's authorization ID). 692 * @param messageSubject The text (if any) that should be used 693 * as the message subject if the delivery 694 * mechanism accepts a subject. This may 695 * be {@code null} if no subject is 696 * required or a subject should be 697 * automatically generated. 698 * @param fullTextBeforeOTP The text (if any) that should appear 699 * before the generated one-time password 700 * in the message delivered to the user 701 * via a delivery mechanism that does not 702 * impose significant constraints on 703 * message size. This may be 704 * {@code null} if no text is required 705 * before the one-time password. 706 * @param fullTextAfterOTP The text (if any) that should appear 707 * after the one-time password in the 708 * message delivered to the user via a 709 * delivery mechanism that does not 710 * impose significant constraints on 711 * message size. This may be 712 * {@code null} if no text is required 713 * after the one-time password. 714 * @param compactTextBeforeOTP The text (if any) that should appear 715 * before the generated one-time password 716 * in the message delivered to the user 717 * via a delivery mechanism that imposes 718 * significant constraints on message 719 * size. This may be {@code null} if no 720 * text is required before the one-time 721 * password. 722 * @param compactTextAfterOTP The text (if any) that should appear 723 * after the generated one-time password 724 * in the message delivered to the user 725 * via a delivery mechanism that imposes 726 * significant constraints on message 727 * size. This may be {@code null} if no 728 * text is required after the one-time 729 * password. 730 * @param preferredDeliveryMechanisms An optional ordered list of preferred 731 * delivery mechanisms that should be 732 * used to deliver the one-time password 733 * to the user. It may be {@code null} 734 * or empty to allow the server to select 735 * an appropriate delivery mechanism. If 736 * it is non-{@code null} and non-empty, 737 * then only the listed mechanisms will 738 * be considered for use, even if the 739 * server supports alternate mechanisms 740 * not included in this list. Each 741 * {@code ObjectPair} item must have 742 * a non-{@code null} value for the first 743 * element, which is the name of the 744 * target delivery mechanism. It may 745 * optionally have a non-{@code null} 746 * value for the second element, which is 747 * a recipient ID to use for that 748 * mechanism (e.g., the target mobile 749 * phone number for SMS delivery, an 750 * email address for email delivery, 751 * etc.). If no recipient ID is provided 752 * for a mechanism, then the server will 753 * attempt to select a value for the 754 * user. 755 * @param controls The set of controls to include in the 756 * request. It may be {@code null} or 757 * empty if no controls should be 758 * included. 759 */ 760 private DeliverOneTimePasswordExtendedRequest(final String authenticationID, 761 final ASN1OctetString staticPassword, final String messageSubject, 762 final String fullTextBeforeOTP, final String fullTextAfterOTP, 763 final String compactTextBeforeOTP, final String compactTextAfterOTP, 764 final List<ObjectPair<String,String>> preferredDeliveryMechanisms, 765 final Control... controls) 766 { 767 super(DELIVER_OTP_REQUEST_OID, 768 encodeValue(authenticationID, staticPassword, messageSubject, 769 fullTextBeforeOTP, fullTextAfterOTP, compactTextBeforeOTP, 770 compactTextAfterOTP, preferredDeliveryMechanisms), 771 controls); 772 773 this.authenticationID = authenticationID; 774 this.staticPassword = staticPassword; 775 this.messageSubject = messageSubject; 776 this.fullTextBeforeOTP = fullTextBeforeOTP; 777 this.fullTextAfterOTP = fullTextAfterOTP; 778 this.compactTextBeforeOTP = compactTextBeforeOTP; 779 this.compactTextAfterOTP = compactTextAfterOTP; 780 781 if ((preferredDeliveryMechanisms == null) || 782 preferredDeliveryMechanisms.isEmpty()) 783 { 784 this.preferredDeliveryMechanisms = Collections.emptyList(); 785 } 786 else 787 { 788 this.preferredDeliveryMechanisms = 789 Collections.unmodifiableList(preferredDeliveryMechanisms); 790 } 791 } 792 793 794 795 /** 796 * Creates a new deliver one-time password extended request from the 797 * information contained in the provided generic extended request. 798 * 799 * @param request The generic extended request to be decoded as a deliver 800 * one-time password extended request. 801 * 802 * @throws LDAPException If a problem is encountered while attempting to 803 * decode the provided generic extended request as a 804 * deliver one-time password extended request. 805 */ 806 public DeliverOneTimePasswordExtendedRequest(final ExtendedRequest request) 807 throws LDAPException 808 { 809 super(request); 810 811 // The request must have a value. 812 final ASN1OctetString value = request.getValue(); 813 if (value == null) 814 { 815 throw new LDAPException(ResultCode.DECODING_ERROR, 816 ERR_DELIVER_OTP_REQ_NO_VALUE.get()); 817 } 818 819 820 // Parse the value. 821 ASN1OctetString password = null; 822 String authnID = null; 823 String subject = null; 824 String fullBefore = null; 825 String fullAfter = null; 826 String compactBefore = null; 827 String compactAfter = null; 828 final ArrayList<ObjectPair<String,String>> pdmList = 829 new ArrayList<ObjectPair<String,String>>(10); 830 try 831 { 832 for (final ASN1Element e : 833 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 834 { 835 switch (e.getType()) 836 { 837 case TYPE_AUTHN_ID: 838 authnID = ASN1OctetString.decodeAsOctetString(e).stringValue(); 839 break; 840 841 case TYPE_PASSWORD: 842 password = ASN1OctetString.decodeAsOctetString(e); 843 break; 844 845 case TYPE_PREFERRED_DELIVERY_MECHANISM_NAMES: 846 final ASN1Element[] mechNameElements = 847 ASN1Sequence.decodeAsSequence(e).elements(); 848 for (final ASN1Element mechElement : mechNameElements) 849 { 850 pdmList.add(new ObjectPair<String, String>( 851 ASN1OctetString.decodeAsOctetString(mechElement). 852 stringValue(), 853 null)); 854 } 855 break; 856 857 case TYPE_PREFERRED_DELIVERY_MECHANISM_NAMES_AND_IDS: 858 final ASN1Element[] pdmElements = 859 ASN1Sequence.decodeAsSequence(e).elements(); 860 for (final ASN1Element pdmElement : pdmElements) 861 { 862 final ASN1Element[] mechElements = 863 ASN1Sequence.decodeAsSequence(pdmElement).elements(); 864 final String mech = ASN1OctetString.decodeAsOctetString( 865 mechElements[0]).stringValue(); 866 867 final String recipientID; 868 if (mechElements.length > 1) 869 { 870 recipientID = ASN1OctetString.decodeAsOctetString( 871 mechElements[1]).stringValue(); 872 } 873 else 874 { 875 recipientID = null; 876 } 877 878 pdmList.add(new ObjectPair<String,String>(mech, recipientID)); 879 } 880 break; 881 882 case MESSAGE_SUBJECT_BER_TYPE: 883 subject = 884 ASN1OctetString.decodeAsOctetString(e).stringValue(); 885 break; 886 887 case FULL_TEXT_BEFORE_OTP_BER_TYPE: 888 fullBefore = 889 ASN1OctetString.decodeAsOctetString(e).stringValue(); 890 break; 891 892 case FULL_TEXT_AFTER_OTP_BER_TYPE: 893 fullAfter = 894 ASN1OctetString.decodeAsOctetString(e).stringValue(); 895 break; 896 897 case COMPACT_TEXT_BEFORE_OTP_BER_TYPE: 898 compactBefore = 899 ASN1OctetString.decodeAsOctetString(e).stringValue(); 900 break; 901 902 case COMPACT_TEXT_AFTER_OTP_BER_TYPE: 903 compactAfter = 904 ASN1OctetString.decodeAsOctetString(e).stringValue(); 905 break; 906 907 default: 908 throw new LDAPException(ResultCode.DECODING_ERROR, 909 ERR_DELIVER_OTP_REQ_UNEXPECTED_ELEMENT_TYPE.get( 910 StaticUtils.toHex(e.getType()))); 911 912 } 913 } 914 } 915 catch (final LDAPException le) 916 { 917 Debug.debugException(le); 918 throw le; 919 } 920 catch (final Exception e) 921 { 922 Debug.debugException(e); 923 throw new LDAPException(ResultCode.DECODING_ERROR, 924 ERR_DELIVER_OTP_REQ_ERROR_PARSING_VALUE.get( 925 StaticUtils.getExceptionMessage(e)), 926 e); 927 } 928 929 if (authnID == null) 930 { 931 throw new LDAPException(ResultCode.DECODING_ERROR, 932 ERR_DELIVER_OTP_REQ_NO_AUTHN_ID.get()); 933 } 934 else 935 { 936 authenticationID = authnID; 937 } 938 939 staticPassword = password; 940 messageSubject = subject; 941 fullTextBeforeOTP = fullBefore; 942 fullTextAfterOTP = fullAfter; 943 compactTextBeforeOTP = compactBefore; 944 compactTextAfterOTP = compactAfter; 945 946 if ((pdmList == null) || pdmList.isEmpty()) 947 { 948 preferredDeliveryMechanisms = Collections.emptyList(); 949 } 950 else 951 { 952 preferredDeliveryMechanisms = Collections.unmodifiableList(pdmList); 953 } 954 } 955 956 957 958 /** 959 * Encodes the provided information into an ASN.1 octet string suitable for 960 * use as the value of this extended request. 961 * 962 * @param authenticationID The authentication ID for the user to 963 * whom the one-time password should be 964 * delivered. It must not be 965 * {@code null}. 966 * @param staticPassword The static password for the user to 967 * whom the one-time password should be 968 * delivered. 969 * @param preferredDeliveryMechanisms The names of the preferred delivery 970 * mechanisms for the one-time password. 971 * It may be {@code null} or empty if the 972 * server should select an appropriate 973 * delivery mechanism. If it is 974 * non-{@code null} and non-empty, then 975 * only the listed mechanisms will be 976 * considered for use, even if the server 977 * supports alternate mechanisms not 978 * included in this list. 979 * 980 * @return An ASN.1 octet string suitable for use as the value of this 981 * extended request. 982 */ 983 private static ASN1OctetString encodeValue(final String authenticationID, 984 final ASN1OctetString staticPassword, 985 final List<String> preferredDeliveryMechanisms) 986 { 987 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(3); 988 989 elements.add(new ASN1OctetString(TYPE_AUTHN_ID, authenticationID)); 990 991 if (staticPassword != null) 992 { 993 elements.add(staticPassword); 994 } 995 996 if ((preferredDeliveryMechanisms != null) && 997 (! preferredDeliveryMechanisms.isEmpty())) 998 { 999 final ArrayList<ASN1Element> dmElements = 1000 new ArrayList<ASN1Element>(preferredDeliveryMechanisms.size()); 1001 for (final String s : preferredDeliveryMechanisms) 1002 { 1003 dmElements.add(new ASN1OctetString(s)); 1004 } 1005 elements.add(new ASN1Sequence(TYPE_PREFERRED_DELIVERY_MECHANISM_NAMES, 1006 dmElements)); 1007 } 1008 1009 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 1010 } 1011 1012 1013 1014 /** 1015 * Encodes the provided information into an ASN.1 octet string suitable for 1016 * use as the value of this extended request. 1017 * 1018 * @param authenticationID The authentication ID for the user to 1019 * whom the one-time password should be 1020 * delivered. It must not be 1021 * {@code null}. 1022 * @param staticPassword The static password for the user to 1023 * whom the one-time password should be 1024 * delivered. It may be {@code null} if 1025 * this request is intended to be used 1026 * to step-up an existing authentication 1027 * rather than perform a new 1028 * authentication (in which case the 1029 * provided authentication ID must match 1030 * the operation's authorization ID). 1031 * @param messageSubject The text (if any) that should be used 1032 * as the message subject if the delivery 1033 * mechanism accepts a subject. This may 1034 * be {@code null} if no subject is 1035 * required or a subject should be 1036 * automatically generated. 1037 * @param fullTextBeforeOTP The text (if any) that should appear 1038 * before the generated one-time password 1039 * in the message delivered to the user 1040 * via a delivery mechanism that does not 1041 * impose significant constraints on 1042 * message size. This may be 1043 * {@code null} if no text is required 1044 * before the one-time password. 1045 * @param fullTextAfterOTP The text (if any) that should appear 1046 * after the one-time password in the 1047 * message delivered to the user via a 1048 * delivery mechanism that does not 1049 * impose significant constraints on 1050 * message size. This may be 1051 * {@code null} if no text is required 1052 * after the one-time password. 1053 * @param compactTextBeforeOTP The text (if any) that should appear 1054 * before the generated one-time password 1055 * in the message delivered to the user 1056 * via a delivery mechanism that imposes 1057 * significant constraints on message 1058 * size. This may be {@code null} if no 1059 * text is required before the one-time 1060 * password. 1061 * @param compactTextAfterOTP The text (if any) that should appear 1062 * after the generated one-time password 1063 * in the message delivered to the user 1064 * via a delivery mechanism that imposes 1065 * significant constraints on message 1066 * size. This may be {@code null} if no 1067 * text is required after the one-time 1068 * password. 1069 * @param preferredDeliveryMechanisms An optional ordered list of preferred 1070 * delivery mechanisms that should be 1071 * used to deliver the one-time password 1072 * to the user. It may be {@code null} 1073 * or empty to allow the server to select 1074 * an appropriate delivery mechanism. If 1075 * it is non-{@code null} and non-empty, 1076 * then only the listed mechanisms will 1077 * be considered for use, even if the 1078 * server supports alternate mechanisms 1079 * not included in this list. Each 1080 * {@code ObjectPair} item must have 1081 * a non-{@code null} value for the first 1082 * element, which is the name of the 1083 * target delivery mechanism. It may 1084 * optionally have a non-{@code null} 1085 * value for the second element, which is 1086 * a recipient ID to use for that 1087 * mechanism (e.g., the target mobile 1088 * phone number for SMS delivery, an 1089 * email address for email delivery, 1090 * etc.). If no recipient ID is provided 1091 * for a mechanism, then the server will 1092 * attempt to select a value for the 1093 * user. 1094 * 1095 * @return An ASN.1 octet string suitable for use as the value of this 1096 * extended request. 1097 */ 1098 private static ASN1OctetString encodeValue(final String authenticationID, 1099 final ASN1OctetString staticPassword, final String messageSubject, 1100 final String fullTextBeforeOTP, final String fullTextAfterOTP, 1101 final String compactTextBeforeOTP, final String compactTextAfterOTP, 1102 final List<ObjectPair<String,String>> preferredDeliveryMechanisms) 1103 { 1104 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(8); 1105 1106 elements.add(new ASN1OctetString(TYPE_AUTHN_ID, authenticationID)); 1107 1108 if (staticPassword != null) 1109 { 1110 elements.add(staticPassword); 1111 } 1112 1113 if (messageSubject != null) 1114 { 1115 elements.add(new ASN1OctetString(MESSAGE_SUBJECT_BER_TYPE, 1116 messageSubject)); 1117 } 1118 1119 if (fullTextBeforeOTP != null) 1120 { 1121 elements.add(new ASN1OctetString(FULL_TEXT_BEFORE_OTP_BER_TYPE, 1122 fullTextBeforeOTP)); 1123 } 1124 1125 if (fullTextAfterOTP != null) 1126 { 1127 elements.add(new ASN1OctetString(FULL_TEXT_AFTER_OTP_BER_TYPE, 1128 fullTextAfterOTP)); 1129 } 1130 1131 if (compactTextBeforeOTP != null) 1132 { 1133 elements.add(new ASN1OctetString(COMPACT_TEXT_BEFORE_OTP_BER_TYPE, 1134 compactTextBeforeOTP)); 1135 } 1136 1137 if (compactTextAfterOTP != null) 1138 { 1139 elements.add(new ASN1OctetString(COMPACT_TEXT_AFTER_OTP_BER_TYPE, 1140 compactTextAfterOTP)); 1141 } 1142 1143 if ((preferredDeliveryMechanisms != null) && 1144 (! preferredDeliveryMechanisms.isEmpty())) 1145 { 1146 final ArrayList<ASN1Element> pdmElements = 1147 new ArrayList<ASN1Element>(preferredDeliveryMechanisms.size()); 1148 for (final ObjectPair<String,String> p : preferredDeliveryMechanisms) 1149 { 1150 if (p.getSecond() == null) 1151 { 1152 pdmElements.add(new ASN1Sequence( 1153 new ASN1OctetString(p.getFirst()))); 1154 } 1155 else 1156 { 1157 pdmElements.add(new ASN1Sequence( 1158 new ASN1OctetString(p.getFirst()), 1159 new ASN1OctetString(p.getSecond()))); 1160 } 1161 } 1162 1163 elements.add(new ASN1Sequence( 1164 TYPE_PREFERRED_DELIVERY_MECHANISM_NAMES_AND_IDS, pdmElements)); 1165 } 1166 1167 return new ASN1OctetString(new ASN1Sequence(elements).encode()); 1168 } 1169 1170 1171 1172 /** 1173 * Retrieves the authentication ID for the user to whom the one-time password 1174 * should be delivered. 1175 * 1176 * @return The authentication ID for the user to whom the one-time password 1177 * should be delivered. 1178 */ 1179 public String getAuthenticationID() 1180 { 1181 return authenticationID; 1182 } 1183 1184 1185 1186 /** 1187 * Retrieves the static password for the user to whom the one-time password 1188 * should be delivered. The returned password may be {@code null} if no 1189 * 1190 * 1191 * @return The static password for the user to whom the one-time password 1192 * should be delivered, or {@code null} if no static password should 1193 * be included in the request. 1194 */ 1195 public ASN1OctetString getStaticPassword() 1196 { 1197 return staticPassword; 1198 } 1199 1200 1201 1202 /** 1203 * Retrieves an ordered list of the names of the preferred delivery mechanisms 1204 * for the one-time password, if provided. 1205 * 1206 * @return An ordered list of the names of the preferred delivery mechanisms 1207 * for the one-time password, or {@code null} if this was not 1208 * provided. 1209 */ 1210 public List<String> getPreferredDeliveryMechanisms() 1211 { 1212 if (preferredDeliveryMechanisms.isEmpty()) 1213 { 1214 return null; 1215 } 1216 else 1217 { 1218 final LinkedHashSet<String> s = 1219 new LinkedHashSet<String>(preferredDeliveryMechanisms.size()); 1220 for (final ObjectPair<String,String> p : preferredDeliveryMechanisms) 1221 { 1222 s.add(p.getFirst()); 1223 } 1224 1225 return Collections.unmodifiableList(new ArrayList<String>(s)); 1226 } 1227 } 1228 1229 1230 1231 /** 1232 * Retrieves an ordered list of the preferred delivery mechanisms that should 1233 * be used to provide the one-time password to the user, optionally paired 1234 * with a mechanism-specific recipient ID (e.g., a mobile phone number for SMS 1235 * delivery, or an email address for email delivery) that can be used in the 1236 * delivery. If this list is non-empty, then the server will use the first 1237 * mechanism in the list that the server supports and is available for the 1238 * target user, and the server will only consider mechanisms in the provided 1239 * list even if the server supports alternate mechanisms that are not 1240 * included. If this list is empty, then the server will attempt to select an 1241 * appropriate delivery mechanism for the user. 1242 * 1243 * @return An ordered list of the preferred delivery mechanisms for the 1244 * one-time password, or an empty list if none were provided. 1245 */ 1246 public List<ObjectPair<String,String>> 1247 getPreferredDeliveryMechanismNamesAndIDs() 1248 { 1249 return preferredDeliveryMechanisms; 1250 } 1251 1252 1253 1254 /** 1255 * Retrieves the text (if any) that should be used as the message subject for 1256 * delivery mechanisms that can make use of a subject. 1257 * 1258 * @return The text that should be used as the message subject for delivery 1259 * mechanisms that can make use of a subject, or {@code null} if no 1260 * subject should be used, or if the delivery mechanism should 1261 * attempt to automatically determine a subject. 1262 */ 1263 public String getMessageSubject() 1264 { 1265 return messageSubject; 1266 } 1267 1268 1269 1270 /** 1271 * Retrieves the text (if any) that should appear before the one-time password 1272 * in the message delivered to the user via a mechanism that does not impose 1273 * significant constraints on message size. 1274 * 1275 * @return The text that should appear before the one-time password in the 1276 * message delivered to the user via a mechanism that does not impose 1277 * significant constraints on message size, or {@code null} if there 1278 * should not be any text before the one-time password. 1279 */ 1280 public String getFullTextBeforeOTP() 1281 { 1282 return fullTextBeforeOTP; 1283 } 1284 1285 1286 1287 /** 1288 * Retrieves the text (if any) that should appear after the one-time password 1289 * in the message delivered to the user via a mechanism that does not impose 1290 * significant constraints on message size. 1291 * 1292 * @return The text that should appear after the one-time password in the 1293 * message delivered to the user via a mechanism that does not impose 1294 * significant constraints on message size, or {@code null} if there 1295 * should not be any text after the one-time password. 1296 */ 1297 public String getFullTextAfterOTP() 1298 { 1299 return fullTextAfterOTP; 1300 } 1301 1302 1303 1304 /** 1305 * Retrieves the text (if any) that should appear before the one-time password 1306 * in the message delivered to the user via a mechanism that imposes 1307 * significant constraints on message size. 1308 * 1309 * @return The text that should appear before the one-time password in the 1310 * message delivered to the user via a mechanism that imposes 1311 * significant constraints on message size, or {@code null} if there 1312 * should not be any text before the one-time password. 1313 */ 1314 public String getCompactTextBeforeOTP() 1315 { 1316 return compactTextBeforeOTP; 1317 } 1318 1319 1320 1321 /** 1322 * Retrieves the text (if any) that should appear after the one-time password 1323 * in the message delivered to the user via a mechanism that imposes 1324 * significant constraints on message size. 1325 * 1326 * @return The text that should appear after the one-time password in the 1327 * message delivered to the user via a mechanism that imposes 1328 * significant constraints on message size, or {@code null} if there 1329 * should not be any text after the one-time password. 1330 */ 1331 public String getCompactTextAfterOTP() 1332 { 1333 return compactTextAfterOTP; 1334 } 1335 1336 1337 1338 /** 1339 * {@inheritDoc} 1340 */ 1341 @Override() 1342 public DeliverOneTimePasswordExtendedResult process( 1343 final LDAPConnection connection, final int depth) 1344 throws LDAPException 1345 { 1346 final ExtendedResult extendedResponse = super.process(connection, depth); 1347 return new DeliverOneTimePasswordExtendedResult(extendedResponse); 1348 } 1349 1350 1351 1352 /** 1353 * {@inheritDoc}. 1354 */ 1355 @Override() 1356 public DeliverOneTimePasswordExtendedRequest duplicate() 1357 { 1358 return duplicate(getControls()); 1359 } 1360 1361 1362 1363 /** 1364 * {@inheritDoc}. 1365 */ 1366 @Override() 1367 public DeliverOneTimePasswordExtendedRequest duplicate( 1368 final Control[] controls) 1369 { 1370 final DeliverOneTimePasswordExtendedRequest r = 1371 new DeliverOneTimePasswordExtendedRequest(authenticationID, 1372 staticPassword, messageSubject, fullTextBeforeOTP, 1373 fullTextAfterOTP, compactTextBeforeOTP, compactTextAfterOTP, 1374 preferredDeliveryMechanisms, controls); 1375 r.setResponseTimeoutMillis(getResponseTimeoutMillis(null)); 1376 return r; 1377 } 1378 1379 1380 1381 /** 1382 * {@inheritDoc} 1383 */ 1384 @Override() 1385 public String getExtendedRequestName() 1386 { 1387 return INFO_DELIVER_OTP_REQ_NAME.get(); 1388 } 1389 1390 1391 1392 /** 1393 * {@inheritDoc} 1394 */ 1395 @Override() 1396 public void toString(final StringBuilder buffer) 1397 { 1398 buffer.append("DeliverOneTimePasswordExtendedRequest(authenticationID="); 1399 buffer.append(authenticationID); 1400 1401 if (messageSubject != null) 1402 { 1403 buffer.append(", messageSubject='"); 1404 buffer.append(messageSubject); 1405 buffer.append('\''); 1406 } 1407 1408 if (fullTextBeforeOTP != null) 1409 { 1410 buffer.append(", fullTextBeforeOTP='"); 1411 buffer.append(fullTextBeforeOTP); 1412 buffer.append('\''); 1413 } 1414 1415 if (fullTextAfterOTP != null) 1416 { 1417 buffer.append(", fullTextAfterOTP='"); 1418 buffer.append(fullTextAfterOTP); 1419 buffer.append('\''); 1420 } 1421 1422 if (compactTextBeforeOTP != null) 1423 { 1424 buffer.append(", compactTextBeforeOTP='"); 1425 buffer.append(compactTextBeforeOTP); 1426 buffer.append('\''); 1427 } 1428 1429 if (compactTextAfterOTP != null) 1430 { 1431 buffer.append(", compactTextAfterOTP='"); 1432 buffer.append(compactTextAfterOTP); 1433 buffer.append('\''); 1434 } 1435 1436 if (preferredDeliveryMechanisms != null) 1437 { 1438 buffer.append(", preferredDeliveryMechanisms={"); 1439 1440 final Iterator<ObjectPair<String,String>> iterator = 1441 preferredDeliveryMechanisms.iterator(); 1442 while (iterator.hasNext()) 1443 { 1444 final ObjectPair<String,String> p = iterator.next(); 1445 buffer.append('\''); 1446 buffer.append(p.getFirst()); 1447 if (p.getSecond() != null) 1448 { 1449 buffer.append('('); 1450 buffer.append(p.getSecond()); 1451 buffer.append(')'); 1452 } 1453 buffer.append('\''); 1454 if (iterator.hasNext()) 1455 { 1456 buffer.append(','); 1457 } 1458 } 1459 } 1460 1461 final Control[] controls = getControls(); 1462 if (controls.length > 0) 1463 { 1464 buffer.append(", controls={"); 1465 for (int i=0; i < controls.length; i++) 1466 { 1467 if (i > 0) 1468 { 1469 buffer.append(", "); 1470 } 1471 1472 buffer.append(controls[i]); 1473 } 1474 buffer.append('}'); 1475 } 1476 1477 buffer.append(')'); 1478 } 1479 }