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