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 }